date
solved in time of CTF
reverse category score

Fichier(s)

Nécessaires

  • Dnspy
  • Windows
  • ExtremeDumper

Flag

NOT FOUND

Description

TSJ's PC is contaminated by malware again! Luckily, TSJ's hacker friends figured out that the malware was nothing more than shenanigans. To be more specific, it seems that the executable is wrapped with layers and layers of onion-like junk files. Can you help TSJ figure out what the malware is doing?

NOTE: on10n.exe is not a real malware. So don't worry :)

Hint

1 | (.exe (.cs (.docm (.vb (.ps1 (.bin (.pdf (.js))))))))

Solution détaillée

Ce challenge est très long car il est décomposé en plusieurs étapes caractéristiques .

  • Etape1

    Ouvrons le binaire sur ghidra :

    Alt text

    On voit plusieurs choses intéressantes à l’importation :

    • Une compilation via Visual Studio
    • Une Destination vers Windows
    • Un Fichier .pdb et un nom original avec l’extension .dll

    Cela ressemble à un programme C# avec .NetCore

    Cela est confirmé par l’analyse de ghidra , en effet celui si n’arrive pas à retrouver une arborescence propre contrairement aux fichiers C par exemple (dans la plupart des cas)

    All text

    On sait aussi que l’on peut coder en C# avec deux packages microsoft différents:

    • .NetFramework
    • .NetCore

    NetCore est plus récente mais elle est plus dur à reverse malheureusement pour nous. La différence majeure qui nous intéresse est que .NetFramework compile le code directement dans un fichier .exe . Tandis que .NetCore compile le code dans une Dll et compile un seconde code microsoft en C qui appelle cette Dll

    Cette nuance est a notée pour la suite.

    Regardons ce que nous donnes DnSpy et DetectItEasy:

    All text

    On observe que le fichier est reconnu comme compilé en C et DnSpy ne décompile rien du tout. Le fichier semble packé.

    Pour rappel , voici un schéma du fonctionnement d’un exécutable packé All text

    Ouvrons l’exécutable et regardons-en Runtime les Dlls qui sont chargés grâce à ExtremeDumper.

    All text

    On voit 5 Dlls drops dans le dossier temporaire. On est donc bien sûr que c’est un outils codé en C# .net

    Toujours avec ExtremeDumper , un peut Dump le process depuis la mémoire . (‘Click Droit’ -> ‘Dump Selected Process’)

    On obtient : All text

    On à toutes les Dll .Net packé qui étaient l’exécutable mais **surtout ; la fameuse dll évoqué précédemment qui contient tous le code C# !!! **

    Analysons on10n.dll

    All text

    On obtient les 2 fonctions si dessous :

    private void MainWindow_Loaded(object sender, RoutedEventArgs e)
    		{
    			IPAddress[] hostAddresses = Dns.GetHostAddresses(Dns.GetHostName());
    			uint seed = 0U;
    			bool flag = false;
    			foreach (IPAddress ipaddress in hostAddresses)
    			{
    				if (ipaddress.AddressFamily == AddressFamily.InterNetwork)
    				{
    					uint num = BitConverter.ToUInt32(ipaddress.GetAddressBytes(), 0);
    					uint num2 = num;
    					List<uint> list = new List<uint>();
    					List<uint> list2 = new List<uint>();
    					uint num3 = 2U;
    					while (num > 1U)
    					{
    						if (num % num3 == 0U)
    						{
    							uint num4 = 0U;
    							while (num % num3 == 0U)
    							{
    								num /= num3;
    								num4 += 1U;
    							}
    							list.Add(num3);
    							list2.Add(num4);
    						}
    						num3 += 1U;
    					}
    					if (this.sameFactor(list, list2))
    					{
    						seed = num2;
    						flag = true;
    						break;
    					}
    				}
    			}
    			if (flag)
    			{
    				MessageBox.Show("OK", "on10n", MessageBoxButton.OK);
    				Random random = new Random((int)seed);
    				byte[] nantouPoliceOfficeBureau = on10n.Properties.Resources.NantouPoliceOfficeBureau;
    				byte[] array2 = new byte[124814];
    				for (int j = 0; j < 124814; j++)
    				{
    					array2[j] = (byte)random.Next(0, 256);
    					byte[] array3 = array2;
    					int num5 = j;
    					array3[num5] ^= nantouPoliceOfficeBureau[j];
    				}
    				return;
    			}
    			MessageBox.Show("Nope", "on10n", MessageBoxButton.OK);
    		}
    

    et

    private bool sameFactor(List<uint> factors, List<uint> exponents)
    		{
    			uint[] array = new uint[]
    			{
    				2U,
    				5U,
    				3371U,
    				30347U
    			};
    			uint[] array2 = new uint[]
    			{
    				1U,
    				1U,
    				1U,
    				1U
    			};
    			if (factors.Count != 4 || exponents.Count != 4)
    			{
    				return false;
    			}
    			for (int i = 0; i < 4; i++)
    			{
    				if (factors[i] != array[i] || exponents[i] != array2[i])
    				{
    					return false;
    				}
    			}
    			return true;
    		}
    
    • Analyse

      Le script récupère les adresses ip et les convertis en INT . Ce INT est passé dans un algorithme qui en sort 2 listes. Ces 2 listes sont comparés avec des listes de référence :

      • [2,5,3371,30347]
      • [1,1,1,1]

      Enfin ; si la clé (INT) est valide , le programme seed un objet random avec cette clé et génère des nombres pseudo-aléatoirement afin de les xor , byte par byte avec un fichier du nom de NantouPoliceOfficeBureau .

      Cherchons cette clé pour retrouver le fichier NantouPoliceOfficeBureau décodé !

  • Méthode 1

Tentons de bruteforce les ips :

public async static Task MainAsync()
     {
         int nbthread = 0;
         all = possibilities();
         Thread counter = new Thread(count);
         counter.Start();

         for (int k = 0; k < 1; k++)
         {
             new Thread((ThreadStart)(async () =>
             {
                 nbthread += 1;
                 while (all.Count > 0)
                 {
                     await semaphoreSlim.WaitAsync();
                     var ip = all[new Random().Next(0, all.Count - 1)];
                     all.Remove(ip);
                     semaphoreSlim.Release();
                     await Test(ip);
                 }
                 nbthread -= 1;
             }))
             {
                 IsBackground = true
             }.Start();
         }
         while (all.Count > 0 || nbthread != 0)
         {
             await Task.Delay(5000);
         }
         Console.WriteLine("Ended !");
         Console.WriteLine();
         Console.ReadLine();
     }
public static List<List<int>> possibilities()
{
    List<List<int>> all = new List<List<int>>();
    for(int i = 0; i < 255; i++)
    {
        for (int j = 0; j < 255; j++)
        {
            List<int> possible = new List<int>();

            possible.Add(192);
            possible.Add(168);
            possible.Add(i);
            possible.Add(j);

            all.Add(possible);
        }
    }

    return all;
}
public static async Task Test(List<int> ip)
{
    var a1 = ip[0];
    var a2 = ip[1];
    var a3 = ip[2];
    var a4 = ip[3];

    byte[] all_bytes = { (byte)a1, (byte)a2, (byte)a3, (byte)a4 };
    uint num = BitConverter.ToUInt32(all_bytes, 0);

    Console.ForegroundColor = ConsoleColor.White;
    var res = encrypt(num);
    var res2 = rev(num).ToString();
    Console.WriteLine("Test  ing: " + res2 + " | Result: ".PadLeft(40-("Test  ing: "+ res2).Length, ' ') + res);
    if (res.Contains("[1 1 1 1 ]") || res.Contains("[2 5 3371 30347 ]"))
    {
        Console.WriteLine("##########################################");
        Console.ForegroundColor = ConsoleColor.Green;
        Console.WriteLine(res);
        Console.WriteLine(rev(num).ToString());
        Console.ReadLine();
        Console.WriteLine("##########################################");
        File.WriteAllText("found.txt", rev(num).ToString());
    }            
}
public static string rev(uint num)
{
    return String.Join(" ", BitConverter.GetBytes(num));
}
private static bool sameFactor(List<uint> factors, List<uint> exponents)
{
    uint[] array = new uint[]
    {
        2U,
        5U,
        3371U,
        30347U
    };
    uint[] array2 = new uint[]
    {
        1U,
        1U,
        1U,
        1U
    };
    if (factors.Count != 4 || exponents.Count != 4)
    {
        return false;
    }
    for (int i = 0; i < 4; i++)
    {
        if (factors[i] != array[i] || exponents[i] != array2[i])
        {
            return false;
        }
    }
    return true;
}
public static string encrypt(uint input)
{
    uint seed = 0U;
    bool flag = false;
    uint num2 = input;

    List<uint> list = new List<uint>();
    List<uint> list2 = new List<uint>();

    uint num3 = 2U;
    while (input > 1U)
    {
        if (input % num3 == 0U)
        {
            uint num4 = 0U;
            while (input % num3 == 0U)
            {
                input /= num3;
                num4 += 1U;
            }
            list.Add(num3);
            list2.Add(num4);
        }
        num3 += 1U;
    }

    if (sameFactor(list, list2))
    {
        seed = num2;
        flag = true;
        Console.WriteLine("Seed FOUND !!!");
        Console.WriteLine(input.ToString());
        Console.ReadLine();
        Console.ReadLine();
        Console.ReadLine();
        Console.ReadLine();
    }

    var res = "[";
    for (int i = 0; i < list.Count; i++)
    {
        res += list[i].ToString() + " ";
    }
    res += "] ";

    var res2 = " [";
    for (int j = 0; j < list2.Count; j++)
    {
        res2 += list2[j].ToString() + " ";
    }
    res2 += "]";

    res += res2.PadLeft(40 - (res).Length, ' ');
    return res;
}

Voici le résultat : All text L’idée était bonne mais malheureusement , on ne peut pas bruteforce , l’ip n’est pas privé donc l’ip ne commence pas par 192.168

  • Méthode 2

    Nous allons inverser le processus afin de trouver la clé valide ; à partir des 2 listes.

    D’abord , j’ai recoder l’algorithme en python pour plus de facilité :

    def cipher(input_):
    	seed = 0
    	flag = False
    	num2 = input_
    
    	list1 = []
    	list2 = []
    
    	num3 = 2
    	while input_ > 1:
    		if(input_ % num3 == 0):
    			num4 = 0
    			while(input_ % num3 == 0):
    				input_ /= num3;
    				num4 += 1
    			list1.append(num3)
    			list2.append(num4)
    		num3 += 1
    
    	return list1,list2
    
    key = 3371 * 30347 # == 1022997370
    
    list1,list2 = cipher(key)
    
    if(list1 == [2,5,3371,30347] and list2==[1,1,1,1]):
    	print(f'Found ! {key}')
    else:
    	print(f'Invalid ... {list1,list2}')
    

    On voit que le script cherche les facteurs multiplicateur de la clé , on trouve :

    key= 3371 * 30347
    #1022997370
    

    On a enfin la clé ! On sauvegarde le fichier à xor NantouPoliceOfficeBureau depuis DnSpy

    J’éxécute ce code :

    public async static Task Xor()
    {
      int code = 1022997370;
      Random random = new Random((int)code);
      byte[] nantouPoliceOfficeBureau = File.ReadAllBytes("NantouPoliceOfficeBureau");
      byte[] array2 = new byte[124814];
      for (int j = 0; j < 124814; j++)
      {
          array2[j] = (byte)random.Next(0, 256);                
          array2[j] ^= nantouPoliceOfficeBureau[j];
      }
      try
      {
          using (var fs = new FileStream("output.docm", FileMode.Create, FileAccess.Write))
          {
              fs.Write(array2, 0, array2.Length);
          }
      }
      catch (Exception ex)
      {
          Console.WriteLine("Exception caught in process: {0}", ex);
      }
      File.WriteAllBytes("output.docm", array2);
    }
    

Enfin ! Nous avons finis l’étape 1. Nous avons un document output.docm valide

Voici le projet final : on10n Project

  • Etape2

On obtient donc un fichier .docm , On suppose donc que le fichier contient une macro VBA à analyser ! Le fichier contient des nombres sur plus de 50 pages … BIZARRE.

Output.docm

Ouvrons les macros du document, on trouve :

Sub AutoOpen()
ibnhEErDWpGoUub
End Sub
Function ibnhEErDWpGoUub()
Dim RkQlPERVCJMmC As String
Dim WKKYbcDtyyJZavE As Long
RkQlPERVCJMmC = ActiveDocument.Name
Dim MxJgtSaOgVVjlz((2 Xor 5)) As String
MxJgtSaOgVVjlz(0) = TEUaYEHeQLSK(Array(((24 + 6) Xor (40 + 35)), ((75 + 7) Xor ((264 - 74) + (54 - 25))), (6 + 47), ((48 - 18) + 12)), Array(((45 - 15) + 72), ((122 + (34 - 15)) Xor 66), (((21 - 2) + 2) Xor 97), ((0 + (0 - 0)) Xor (0 + 105))))
MxJgtSaOgVVjlz(1) = TEUaYEHeQLSK(Array(98, 19, 2, ((42 - 0) Xor (47 + (39 - 9)))), Array(84, ((31 - 3) + 11), 64, 34))
MxJgtSaOgVVjlz(2) = TEUaYEHeQLSK(Array(((73 - 27) Xor 134), (16 Xor (148 - 48)), (((17 - 4) + (83 - 34)) Xor 85), (6 + 16)), Array(238, (73 - 4), (41 + (8 - 2)), ((43 - 18) Xor (95 - 38))))
MxJgtSaOgVVjlz((3 - 0)) = TEUaYEHeQLSK(Array((260 - 102), ((8 + 1) Xor (14 + (70 - 8))), 91), Array((82 Xor ((336 - 103) + 16)), (2 + 1), (16 Xor 126)))
MxJgtSaOgVVjlz((0 Xor (7 - 3))) = TEUaYEHeQLSK(Array((39 Xor 165), (24 - 9), (124 - 46), 200), Array(((26 + 149) Xor 108), ((78 - 31) Xor (11 + 7)), 127, (211 Xor (4 + 41))))
MxJgtSaOgVVjlz((3 + 2)) = TEUaYEHeQLSK(Array((214 - 32), ((29 - 0) + (54 - 22)), 88, (46 Xor ((19 - 0) + 83))), Array(130, 9, (76 Xor 39), (((8 - 4) + 1) Xor (3 + 11))))
MxJgtSaOgVVjlz(((2 - 1) Xor 7)) = TEUaYEHeQLSK(Array((70 Xor 63), (43 Xor 94), (122 + (42 - 5)), ((106 - 1) + 11)), Array(((47 - 10) + 21), (32 + (55 - 18)), ((66 - 17) + 173), (80 - 4)))
For i = 1 To (13 - 6)
Dim RvvwCstwOcTmn
RvvwCstwOcTmn = Mid(RkQlPERVCJMmC, 3 * (i - ((0 + 0) Xor (1 - 0))) + 1, (6 - 3))
If bTkfDsyoeYKIp(RvvwCstwOcTmn) <> MxJgtSaOgVVjlz(i - ((0 - 0) Xor (1 + 0))) Then
MsgBox TEUaYEHeQLSK(Array((183 Xor (10 + 3)), ((128 - 57) + 76), 128, 48), Array(244, (74 Xor 182), (154 Xor (98 + 8)), 85))
Exit Function
Else
For JLiFGdziyvzxk = (0 + 1) To (1 + 2)
WKKYbcDtyyJZavE = WKKYbcDtyyJZavE + Asc(Mid(RvvwCstwOcTmn, JLiFGdziyvzxk, 1))
Next JLiFGdziyvzxk
End If
Next i
MsgBox TEUaYEHeQLSK(Array(((4 + 82) Xor 213), ((2 - 0) + (7 - 2))), Array((386 - 182), (3 Xor 79)))
Dim UrsbmGCGEiBd, XERrezCMaATcbc As String
Dim KwIZUgkZxPdq() As String
UrsbmGCGEiBd = ActiveDocument.Content
KwIZUgkZxPdq = Split(UrsbmGCGEiBd, TEUaYEHeQLSK(Array((((27 - 13) + (125 - 43)) Xor 248)), Array(180)))
For i = (2 - 1) To (UBound(KwIZUgkZxPdq) - LBound(KwIZUgkZxPdq) + (0 + (1 - 0)))
Dim rhsuhOjUSqGaYZ As Long
rhsuhOjUSqGaYZ = CInt(KwIZUgkZxPdq(i - 1)) Xor (WKKYbcDtyyJZavE Mod &H100)
XERrezCMaATcbc = XERrezCMaATcbc & Chr(rhsuhOjUSqGaYZ)
WKKYbcDtyyJZavE = (WKKYbcDtyyJZavE * rhsuhOjUSqGaYZ + &H3DC) Mod &H10000
Next i
End Function
Function bTkfDsyoeYKIp(t)
Dim kLHRAxWNuYJTX, i, n, HEikuaZGmGupLJg As Integer
HEikuaZGmGupLJg = &HFFFF
For n = ((2 - 1) Xor (0 + (0 - 0))) To Len(t)
Dim JLiFGdziyvzxk, m
m = Asc(Mid(t, n, ((0 + (0 - 0)) Xor (0 + 1))))
HEikuaZGmGupLJg = HEikuaZGmGupLJg Xor m
For JLiFGdziyvzxk = 1 To 8
If HEikuaZGmGupLJg / (3 - 1) <> Int(HEikuaZGmGupLJg / ((0 - 0) Xor 2)) Then
kLHRAxWNuYJTX = &HA001
Else
kLHRAxWNuYJTX = 0
End If
HEikuaZGmGupLJg = Int(HEikuaZGmGupLJg / ((1 - 0) Xor 3)) And &H7FFF
HEikuaZGmGupLJg = HEikuaZGmGupLJg Xor kLHRAxWNuYJTX
Next JLiFGdziyvzxk
Next n
bTkfDsyoeYKIp = Hex$(HEikuaZGmGupLJg)
End Function
Private Function TEUaYEHeQLSK(gzlgDFqcaZbl As Variant, dUjzfEheqoBKBi As Variant)
Dim BxjseuZvvEWh As String
BxjseuZvvEWh = ""
For i = LBound(gzlgDFqcaZbl) To UBound(gzlgDFqcaZbl)
BxjseuZvvEWh = BxjseuZvvEWh & Chr(dUjzfEheqoBKBi(i) Xor gzlgDFqcaZbl(i))
Next
TEUaYEHeQLSK = BxjseuZvvEWh
End Function

C’est illisible , nous allons donc renommer les variables , les fonctions , simpflier les codes basiques comme (((1 - 0) Xor 3)) And &H7FFF)

On a :

Sub AutoOpen()
test
End Sub
Function test()
MsgBox (0) Xor (1)
End Function

Function ibnhEErDWpGoUub()
Dim var2 As String
Dim variable2 As Long
var2 = ActiveDocument.Name
Dim var1(7) As String
var1(0) = "3FAC"
var1(1) = "64BE"
var1(2) = "F1D6"
var1(3) = "5F5"
var1(4) = "A216"
var1(5) = "443C"
var1(6) = "C0A8"
For i = 1 To (7)
Dim variable1
variable1 = Mid(var2, 3 * (i - 1) + 1, 3)
If fonction1(variable1) <> var1(i - (1)) Then
MsgBox "Nope"
Exit Function
Else
For varibale3 = (1) To (3)
variable2 = variable2 + Asc(Mid(variable1, varibale3, 1))
Next varibale3
End If
Next i
MsgBox "OK"
Dim variable11, variable4 As String
Dim variable10() As String
variable11 = ActiveDocument.Content
variable10 = Split(variable11, ",")
For i = (1) To (UBound(variable10) - LBound(variable10) + (1))
Dim variable9 As Long
variable9 = CInt(variable10(i - 1)) Xor (variable2 Mod &H100)
variable4 = variable4 & Chr(variable9)
variable2 = (variable2 * variable9 + &H3DC) Mod &H10000
Next i
End Function
Function fonction1(t)
Dim variable8, i, n, variable7 As Integer
variable7 = &HFFFF
For n = (1) To Len(t)
Dim varibale3, m
m = Asc(Mid(t, n, 1))
variable7 = variable7 Xor m
For varibale3 = 1 To 8
If variable7 / (2) <> Int(variable7 / 2) Then
variable8 = &HA001
Else
variable8 = 0
End If
variable7 = Int(variable7 / 2) And &H7FFF
variable7 = variable7 Xor variable8
Next varibale3
Next n
fonction1 = Hex$(variable7)
End Function
  • Analyse

Le nom du fichier est enregistré dans une variable (var2):

var2 = ActiveDocument.Name

Ensuite , il va découpé par blocks de 3 la string et la passer dans un algorithme . Chaque blocks est comparés a ces blocks de références:

var1(0) = "3FAC"
var1(1) = "64BE"
var1(2) = "F1D6"
var1(3) = "5F5"
var1(4) = "A216"
var1(5) = "443C"
var1(6) = "C0A8"

On peut réecrire cet algorithme en python et bruteforce tous les blocks pour avoir la bonne combinaison !

import string

var1 = ["3FAC","64BE","F1D6","5F5","A216","443C","C0A8"]
charset = string.printable

def negativedecTohex(val, nbits):
	return hex((val + (1 << nbits)) % (1 << nbits))

def decTohex(n):
  	conversion_table = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A' , 'B', 'C', 'D', 'E', 'F']
  	if(n<=0):
  		return negativedecTohex(n,16).lstrip('0x').upper()
  	remainder = n%16
  	return  decTohex(n//16)+conversion_table[remainder]

def cypher(block):
  	var7 = -1
  	#print('Testing block: '+block)
  	for k in range(len(block)):
  		var8 = 0
  		m = ord(block[k])
  		var7 = var7 ^ m
  		for j in range(8):
  			if(var7 % 2 ==1 ):
  				var8 = -24575
  			else:
  				var8 = 0
  			var7 = (var7//2) & 32767
  			var7 = var7 ^ var8
  	return decTohex(var7)

  def bruteblock():
  	name = ''
  	index = 0
  	while(True):
  		for a in charset:
  			for b in charset:
  				for c in charset:
  					if(index==len(var1)):
  						return name
  					block = a+b+c
  					resp = cypher(block)
  					if(resp == var1[index]):
  						name += block
  						index += 1
  						print(name)
  						break
  		print('Block Not Foud ...')
  		break
  	return name

  name = bruteblock()
  print("#"*10)
  print(f"# FileName: {name}")
  print("#"*10)

  for i in range(7):
  	block = name[3*(i):3*(i+1)]
  	resp = cypher(block)
  	if(var1[i] != resp):
  		print(f'Invalid BLock: {block}({resp}) !={var1[i]}')
  		break
  	else:
  		print(f'Valid Block : {block}({resp}) =={var1[i]}')

Enfin :

All text

Nous avons la clé : co<e)eltoq3is5bu.Wwcg

On peut donc la remplacer dans le script et continuer ! On obtient la MsgBox “OK” ! Tous semble valide.

En executant le script; on obtient un Xor entre les lettres ascii du fichiers et la clé .

On en sort ce code :

  'ÞÚ%çÊúe SYStem.IO.CoMPrESsion.deFLATeStreAm( [SystEm.IO.MEMoRysTREAM][cOnvErt]::froMBase64STRiNg(' . BASE64 .') , [System.IO.cOMprEssION.coMpRessIonmODe]::decOMPresS ) |fOReAch-ObJEcT{ neW-oBJECt IO.StReaMReAdeR( $_ , [TeXT.eNCoDING]::ascIi ) } | fOreacH-OBjECT { $_.ReaDToenD( )} )| & ( $PshomE[21]+$PSHOme[34]+'X')

On obtient ce code powershell :) . Le Hint nous confirme que c’est bien un fichier ps1

  • Etape3

On peut réordonner le script de la manière suivante :

$base64data = " BIG BASE64 U KNOW "

$data = [System.Convert]::FromBase64String($base64data)
$ms = New-Object System.IO.MemoryStream
$ms.Write($data, 0, $data.Length)
$ms.Seek(0,0) | Out-Null

$defl = New-Object System.IO.Compression.DeflateStream($ms, [System.IO.Compression.CompressionMode]::Decompress)

$sr = New-Object System.IO.StreamReader($defl, [TeXT.eNCoDING]::ascIi)

fOreacH-OBjECT{
	$sr.ReaDToenD( )
}| & Write-Output # = ieX

Voici le premier script complet : decode1.ps1

La prochaine étape est de remplacer la commande d’éxecution par Write-Ouput . Ainsi , le code décodé sera afficher à la place d’être éxécuté .

All Text

On peut simplifier rapidement et on trouve 2 choses importantes :

  • Un ShellCode qui est chargé dans la mémoire grâce à la fonction : Virtual-Invoke
  • Une vérification du chemin d’accés grâce à un Xor.

En effet notre shellcode est Xoré avec un byte inconnu avant son exécution.

On à :

${cuRrloCaTIoNOBject} = &("Get-Location");
${cURrloCation} =${cuRRlOcaTIOnobJect}."PaTH".("ToCharArray").Invoke();
${DeSTLOCATion} = @(0x20,0x17,0x39,0x32,0x50,0x42,0x4f,0x50,0x39,0x51,0x50,0x47,0x40,0x51,0x43,0xf,0xd,0xf,0xf,0x39,0x4c,0x4b,0xe,0xd,0x4b);
${acCUM} = 0;

 if (${CurRlOcAtIon}."LENgth" -ne${dEStLocAtioN}."leNGTH") { & ("Echo Nope");
 exit } for (${nUm} = 0;
${nUm} -lt${curRLocaTIon}."lEngTH";
${NUm}++) {${cHar} = ((${cuRRlOCATioN}[${NUm}].ToInt16(${nUlL}) + 0xdd) -band 0xff);
 if (${ChAR} -ne${dEsTLocATIOn}[${NUm}]) { &('echo Nope');
 exit } else {${acCUm} = (${AcCuM} +${chAR}) -band 0xff } };

On peut le traduite en python :

def check(key):
	destlocation = [0x20,0x17,0x39,0x32,0x50,0x42,0x4f,0x50,0x39,0x51,0x50,0x47,0x40,0x51,0x43,0xf,0xd,0xf,0xf,0x39,0x4c,0x4b,0xe,0xd,0x4b]
	if(len(key) != len(destlocation)):
		print("Invalid Lenght ! :(")
		return False
	else:
		accum = 0
		for i in range(len(key)):
			if(destlocation[i] != (ord(key[i])+0xdd-1) % 0xff ):
				print("Nope !")
		print('Valid Key !')
		return True

key = "IdkNowTheKeyForNow:("
check(key)

On peut inverser le processus et obtenir la clé :

destlocation = [0x20,0x17,0x39,0x32,0x50,0x42,0x4f,0x50,0x39,0x51,0x50,0x47,0x40,0x51,0x43,0xf,0xd,0xf,0xf,0x39,0x4c,0x4b,0xe,0xd,0x4b]


def getkey():
	key = ''
	for i in range(len(destlocation)):
		key += chr((destlocation[i]-0xdd +1)%0xff)
	return key


def check(key):
	if(len(key) != len(destlocation)):
		print("Invalid Lenght ! :(")
		return False
	else:
		accum = 0
		for i in range(len(key)):
			if(destlocation[i] != (ord(key[i])+0xdd-1) % 0xff ):
				print("Nope !")
				return False
		print(f'Valid Key ! | {key}')
		return True

check(getkey())

Output:

root@DESKTOP-HNQJECB: /c/RevDotNet
➜   python3 location.py
Valid Key ! | C:\Users\tsjctf2022\on10n

Nous avons le bon chemin d’accés : C:\Users\tsjctf2022\on10n

On à ensuite :

for (${nUM} = 0;${NUM} -lt${sheLLCodE}."LeNgTH";${NUm}++)
{
  ${sHelLcode}[${NUm}] =${shELlCODe}[${nUM}] -bxor${ACCUm}
}

Et ACCUm est généré à partir du chemin d’accés trouvé plus tôt. Dans le code powershell précédent :

else {${acCUm} = (${AcCuM} +${chAR}) -band 0xff }

On peut donc mettre à jour notre script powershell pour obtenir la clé de xor : 56 soit , 0x38

${cuRrloCaTIoNOBject} = &("Get-Location");
${cURrloCation} = "C:\Users\tsjctf2022\on10n".("ToCharArray").Invoke();

${DeSTLOCATion} = @(0x20,0x17,0x39,0x32,0x50,0x42,0x4f,0x50,0x39,0x51,0x50,0x47,0x40,0x51,0x43,0xf,0xd,0xf,0xf,0x39,0x4c,0x4b,0xe,0xd,0x4b);
${acCUM} = 0;

 if (${CurRlOcAtIon}."LENgth" -ne${dEStLocAtioN}."leNGTH") { &("{1}{0}" -f 'o','ech') ("Nope");
 exit } for (${nUm} = 0;
${nUm} -lt${curRLocaTIon}."lEngTH";
${NUm}++) {${cHar} = ((${cuRRlOCATioN}[${NUm}].ToInt16(${nUlL}) + 0xdd) -band 0xff);
 if (${ChAR} -ne${dEsTLocATIOn}[${NUm}]) { &("{0}{1}" -f 'ech','o') ("{0}{1}" -f 'Nop','e');
 exit } else {${acCUm} = (${AcCuM} +${chAR}) -band 0xff } };

Write-Output $AcCuM

Aprés un Xor , on obtient ce shellcode.

On peut donc en faire un binaire de la manière suivante :

#include <stdio.h>
int main() {
    getchar();
    char shellcode[] = " ... SHELLCODE ... ";
    int (*func)(void) = (int(*)(void))shellcode;
    func();
    return 0;
}

Output :

root@DESKTOP-HNQJECB: /c/RevDotNet
➜   gcc c.c -o c
root@DESKTOP-HNQJECB: /c/RevDotNet
➜   file c
c: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=45899d7010ccd8ec89e3e4b6d636f096e7a69aa9, for GNU/Linux 3.2.0, not stripped
root@DESKTOP-HNQJECB: /c/RevDotNet
  • Etape4