Algoritma enkripsi yang digunakan adalah melakukan xor dengan source code dari file enkripsi itu sendiri. Berikut solver yang kami gunakan
import base64x = base64.b64decode("bm\x4avdHh\x71Z3VtbnY9X\x31\x39\x70bXBvcn\x52fXyg\x6eX\x48g2Zlx\x34N\x7aM\x6eLCB\x66X2J\x31aWx0a\x575zX18u\x5819kaWN\x30X19\x62\x4a2dceDZjb2J\x68\x58Hg\x32Y3\x4dnXS\x67\x70LCAg\x5819idW\x6cs\x64Glu\x63\x319f\x4cl9f\x5a\x47lj\x64F\x39fWy\x64\x63eDZ\x6ab2N\x68XH\x67\x32Y3M\x6e\x58SgpK\x54t\x6bb2\x463dW\x70\x69aG5kPV9\x66a\x571wb3\x4a0X18oJ1x4N\x6dZ\x7aJywgX\x31\x39idWl\x73dGluc\x319\x66Ll\x39fZGl\x6adF9fWyd\x6eXHg2Y\x329\x69YVx4N\x6d\x4ezJ\x310oKSw\x67\x49F9fYnVpbHRpbnNf\x58y\x35\x66X2\x52pY\x33RfX1\x73nXHg2Y29jY\x56x4NmNzJ10oKS\x6b7YmV\x6ae\x48N6c3B\x6bb\x32t\x75\x62ndjPW9\x77Z\x574\x6fZ\x58Zhb\x43giXHg\x31\x5a\x6cx\x34\x4e\x57Zc\x65DY2XHg2\x4fVx\x34NmM\x69\x4b\x79JceD\x591XHg1\x5alx4NWYiKSkucmV\x68ZCg\x70Cgp\x6d\x623IgbHZlZW\x6cp\x63\x471uc3R5an\x42pLCB\x77YnZt\x64\x6d\x4e4a\x47\x352Ym\x39hZ\x57os\x49Gxi\x5aWt3Y3NrZHZlZ\x32J\x6b\x65CBpb\x69BuY\x6d90e\x47p\x6edW1\x75d\x69\x353\x59WxrKG5ib3R4amd1b\x575\x32Lm\x64\x6c\x64GN3Z\x43\x67\x70KToKI\x43AgIGZ\x76\x63i\x42venBubXJ\x6dcm\x4evY\x58N5Y\x33\x45\x67aW4gbGJla3djc2\x74k\x64mVnY\x6d\x524Ogog\x49\x43AgICAgIGlmI\x475v\x64CB\x76enBubXJm\x63mNv\x59XN5Y3\x45uZW5kc3d\x70dGgoIlx4MmV\x63eDc\x77X\x48g\x33OSIpO\x67\x6fgI\x43\x41gICAgIC\x41gI\x43Bp\x63\x47\x70\x7ac2NyZWh2eW5\x6eYXY9b3Blb\x69\x68sdmVlaWlwbW5zd\x48lqc\x47\x6brI\x6cx4\x4dmYiK296cG5tcmZ\x79Y29hc3ljcS\x77g\x49\x6cx4Nz\x4a\x63\x65D\x59yIikucm\x56hZC\x67pO\x33J\x6e\x65WlsdndzcmRjZG5ld\x441vcGVuKGx\x32ZWV\x70aXBt\x62\x6eN\x30e\x57pwa\x53siXHgyZ\x69\x49rKG96cG5\x74cmZyY2\x39hc3\x6cjcS5yc3BsaXQoIi4iLCA\x78KVswXSk\x72Ii\x35ceDY4XHg2MVx4\x4ejNceDZiXHg2NV\x784\x4e\x6aRce\x44Zj\x58Hg2\x5al\x784\x4e\x6dMi\x4cC\x41iXHg3N1\x78\x34\x4ej\x49\x69KQogICA\x67I\x43\x41\x67IC\x41g\x49\x43Bmb\x33\x49gaG5wcGN\x33Zmp\x32c\x321\x6acW\x56\x68I\x47\x6cuIHJh\x62m\x64lKGxlbi\x68\x70cGpz\x632NyZWh2e\x57\x35nYX\x59pK\x54o\x4bIC\x41gICA\x67\x49C\x41gICAgICAgIHJneW\x6c\x73\x64ndzcm\x52\x6aZG\x35ldC\x353cml0\x5a\x53hj\x61\x48I\x6f\x61XBqc3NjcmVodnlu\x5a\x32F2W2\x68\x75cH\x42jd2\x5a\x71dnN\x74\x593FlYV\x31eb3\x4akK\x47\x4al\x593hzen\x4ewZG9\x72bm53Y1soaG5wcGN\x33\x5a\x6dp\x32c21j\x63\x57VhKjB4MjcpJ\x57xlbihiZWN\x34c\x33p\x7ac\x47Rva\x325ud2M\x70XSk\x70LmV\x75\x5929kZS\x67p\x4b\x51o\x67IC\x41gICAgI\x43AgICBuYm90eGpnd\x571udi5yZW1vdm\x55ob\x48Z\x6cZWl\x70cG1u\x633R5anBpK\x79\x4aceDJmIi\x74ve\x6eB\x75bXJmcmNvY\x58N5Y3EpCgp\x6bb\x32F3dWpi\x61\x475\x6bL\x6eJlbW92Z\x53hldmFsK\x43Jc\x65D\x56\x6dXHg1Zl\x784NjZceD\x595XH\x672Y\x79IrIlx4NjVceDV\x6dXHg1Z\x69IpK\x51\x3d\x3d")
f =open("important_file.hackedlol", "rb").read()result =b""for i inrange(len(f)): result +=bytes([f[i] ^ x[(i*0x27)%len(x)]])print(result)
Flag : COMPFEST15{b1G_brr41nz_us1ng_c0d3_4s_k3y_8d7113ecc1}
KatVM
Description
-
Solution
Diberikan file pyc, selanjutnya lakukan decompile dengan pycdc. Karena ada beberapa kode yang invalid, maka lakukan perbaikan manual. Berikut hasil perbaikannya
katvm.py
import sysimport tracebackfrom utils import is_eof, read_instruction, help_exitfrom vm import KatVMdefrun(execfile=None): vm =KatVM() f =open(execfile, 'rb') skip_next =FalsewhileTrue: (cmd, arg) =read_instruction(f)if skip_next: skip_next =Falsecontinue func = cmdif arg !='':if(func ==0): res = vm.left(arg)elif(func ==1): res = vm.right(arg)elif(func ==2): res = vm.store(arg)elif(func ==6): res = vm.popeq(arg)elif(func ==7): res = vm.exit(arg)else:print('arg ????')if res ==True: skip_next =Trueelse:if(func ==3): res = vm.print()elif(func ==4): res = vm.input()elif(func ==5): res = vm.push()elif(func ==7):print("end", func)exit()# res = vm.exit()else:print(func, 'none ???')ifis_eof(f): f.close()returnNonedefmain():iflen(sys.argv)!=2:help_exit()try:run(sys.argv[1])finally:print('Segmentation fault')main()
classKatVM: tape: list[str]= [''] memory: list[str]= [] pointer:int=0defleft(self=None,value=None): val =int(value)for _ inrange(val):if self.pointer ==0: self.tape.insert(0, '')continue self.pointer -=1defright(self=None,value=None): val =int(value)for _ inrange(val):if self.pointer ==len(self.tape)-1: self.tape.append('') self.pointer +=1defstore(self,string=None):for i inrange(len(string)): self.tape[self.pointer]= string[i] self.right(1) self.tape[self.pointer]=''defprint(self):# print("tape",self.tape) c = self.tape[self.pointer]while c:print(c, end ='', flush=True) self.right(1) c = self.tape[self.pointer]if c =='':print('')breakdefinput(self): self.store(input())defpush(self): self.memory.append(self.tape[self.pointer])defpopeq(self=None,value=None): tmp = self.memory.pop()return tmp == value
Karena hanya ada 1 fungsi pengecekan yaitu popeq dan tidak ada fungsi operasi aritmatika maka kita lakukan print saja pada popeq untuk cek nilai yang dibandingkan. Dari 2 percobaan dengan nilai yang berbeda didapatkan perbandingan untuk 2 nilai yang berbeda juga namun di index yang sama, jadi tinggal lakukan bruteforce saja.
from pwn import*import stringinp =list(string.printable[:-6])ori =list(string.printable[:-6])context.log_level ='error'for i inrange(64): r =process(["python3", "katvm.py", "../check.kb"]) r.recvuntil(b"secret!") r.sendline(''.join(inp).encode()) r.recvline()for j inrange(i+1): tmp = r.recvline().strip().decode().split(' ') index = ori.index(tmp[0]) inp[index]= tmp[1] r.close()# print(tmp)print(''.join(inp))
Sedikit perbaikan pada format flag dan dapat flag
Flag : COMPFEST15{r3Ad1ng_byt3C0de_c4n_b3_r3ally_H4rd_y0u_kNow}
GoDroid
Description
-
Solution
Diberikan file apk
Input yang kita masukkan pada aplikasi akan dibandingkan dengan string sepanjang 100 digit atau kalau diubah ke bytes sebesar 50 bytes. Dari analisis didapatkan bahwa hasil enkripsi dari input panjangnya sama dengan input. Proses enkripsi dilakukan di libgojni.so, pada fungsi utils_Encrypt dilakukan pemanggilan fungsi utils_F terlebih dahulu.
Dari hasil percobaan pada golang (compile - decompile) diketahui bahwa nilai seednya adalah 0 (register x1 pada address 0xd0bc8). Selanjutnya dilakukan generate nilai random dengan randInt dan menggunakan nilai v9 sebagai argument yang mana nilai v9 terakhir adalah panjang dari input. Nilai dari v9 akan dilakukan decrement pada setiap looping.
Pada saat percobaan dengan frida didapatkan perubahan satu byte pada input hanya berdampak pada satu byte pada nilai enkripsi dengan panjang yang sama. Hal tersebut menandakan bahwa terdapat pengacakan index pada input. Dengan adanya nilai yang digenerate sesuai dengan maximal index maka kami asumsikan bahwa nilai random yang digenerate akan digunakan sebagai index. Dari hasil percobaan dengan frida didapatkan bahwa asumsi tersebut benar. Selanjutnya dari hasil generate 50 nilai random terdapat nilai random yang sama dan jika digunakan sebagai index maka tidak invertible jika kita ingin mendapatkan input aslinya, dengan beberapa analisis dan percobaan didapatkan bahwa input yang telah dipetakan akan dihapus sehingga ada perubahan index nantinya
Input = “abcdef”
Index -> 3
Result = “d”
Input = “abcef”
Index -> 4
Result = “df”
v85 kemungkinan adalah nilai yang sudah dipetakan dan dixor dengan suatu nilai yang kami tidak tahu gimana memanggil fungsinya. Tapi karena sudah mengetahui flow sebagian besar, bagian xor bisa di reverse saja untuk mendapatkan nilai xornya.
Berikut solver yang kami gunakan. Dapatkan nilai untuk leak key dan pemetaan (sekaligus memastikan nilai xor static)
packagemainimport ("fmt""math/rand")funcmain(){ rand.Seed(0)for i :=50; i >0; i-- { fmt.Printf("%v, ", rand.Intn(i)) }}
Setelah memiliki data yang dibutuhkan, tinggal dapatkan static value(xor) lalu reverse pemetaan
import stringori =list(string.printable[:50])ori_2 = ori[::-1]mapped =""mapped_index = [24,2,25,2,7,16,31,26,24,16,8,14,21,17,24,11,7,5,2,2,5,4,18,15,13,11,16,20,17,4,17,11,5,2,6,13,12,8,5,0,7,3,4,3,1,2,0,0,0,0]for i inrange(len(mapped_index)): mapped += ori[mapped_index[i]]del ori[mapped_index[i]]xored_val =bytes.fromhex("33536753fee2705bcc7aae9e0c04bea12e96c657bbce6db43d38d43417f3b6f42120e72f3ea5e45deffc94f8e90072ce6c64")xored_val_2 =bytes.fromhex("2c2d782b82fd5144d3658f81131b9fbe0be6b82b9eef11ab2227a44a6becceeb3e5ef8534080fb2391e38be797250ed1121a")target =bytes.fromhex("650e2014a6d7041d8024a8984e47cc9810cead06b0c24dfc742aa71c6de29cb42679b1544286ed09cbf2d2bebd7c2ccd1148")key = []for i inrange(len(xored_val)): key.append(xored_val[i] ^ord(mapped[i]))mapped = []for i inrange(len(xored_val)): mapped.append(chr(xored_val_2[i] ^ key[i]))mapped_flag = []for i inrange(len(target)): mapped_flag.append(target[i] ^ key[i])flag = [0for i inrange(len(mapped))]for i inrange(len(mapped)): flag[ori_2.index(mapped[i])]= mapped_flag[i]print(''.join(map(chr,flag)))
Flag : COMPFEST15{doot_doola_doot_doo_5bd89375a2941192b618eb4536ad6b}
Validator Machine
Description
-
Solution
Diberikan file elf 64 bit. Berikut potongan kode fungsi pertama yang dipanggil untuk mengecek input kita
Step in pada salah satu fungsi pengecekan, contoh pada pemanggilan fungsi di address 0x023C89A.
Fungsi terakhir yang dipanggil akan seperti gambar diatas. Step out dua kali dan cek pada assembly, maka akan ada dua perbandingan yaitu nilai r14 dan r15 yang didapat dari lzcnt dan tzcnt. Jadi intinya lzcnt dan tzcnt menghitung null bytes pada lsb dan msb
Sedangkan bextr melakukan extraksi bit r9d dengan ketentuan r10d.
r10d = 0000011000000000 -> 00000110 00000000
start = 00000000
length = 00000110
r9d = 0x6261 -> 0b110001001100001
6 bit from index 0
result = 100001
Karena terdapat bextr untuk 1 bit dan nilainya pasti, maka kita bisa reverse validasi flag dengan hanya memanfaatkan bextr 0x10* saja. Pada proses pengerjaan, kami mendapat 2 kali fake flag sebelum akhirnya dapat flag asli. Untuk mendapatkan flag asli pertama kita perlu mengetahui dimana perubahan pada program
Pada address 0x023C89A terdapat cross reference dari sub_23CCD5. Fungsi tersebut memiliki cross reference dari .init_array yang mana artinya dipanggil sebelum main.
Pada salah satu instruksi terdapat pemanggilan fungsi yang nilai addressnya disimpan pada register. Selanjutnya lakukan breakpoint dan debug pada address tersebut.
Fungsi tersebut ternyata melakukan pemanggilan syscall dengan rax = 0x65 yang mana itu merupakan nilai untuk ptrace
Jadi pemanggilan fungsi tersebut tujuannya adalah untuk melakukan pengecekan terhadap debugging/anti debugging dan pada langkah tersebutlah dilakukan perubahan flow/pemanggilan fungsi yang seharusnya. Jadi langkah yang kami lakukan adalah melakukan perubahan terhadap nilai eax yang dibandingkan menjadi 0 lalu melakukan reverse terhadap validasi flag (dengan appropch yang disampaikan sebelumnya, bextr dengan r10d == 0x10*) untuk mendapatkan flagnya. Untuk sampai ke instruksi validasi dengan hasil extract bit 0x10* dilakukan step in secara terus menerus sampai ketemu instruksi tersebut. Berikut solver yang kami gunakan
#!/usr/bin/python3import stringfrom itertools import productfrom Crypto.Util.number import*classSolverEquation(gdb.Command):def__init__ (self):super(SolverEquation, self).__init__ ("solve-equation",gdb.COMMAND_OBSCURE)defget_flag(self,f): f = f.split('\n') counter =0 bin_val =''for i inrange(len(f)):if('or r11,0x0'in f[i]):if(counter ==0):if('r14,0xf'in f[i-2]): bin_val +='1'else: bin_val +='0' counter +=1if(len(bin_val)==16):returnlong_to_bytes(int(bin_val[::-1],2))[::-1] bin_val =''else: counter =0definvoke (self,arg,from_tty): gdb.execute("pie del") bp = [0x000000000023C89A,0x000000000023C8AA,0x000000000023C8BA,0x000000000023C8CA,0x000000000023C8DA,0x000000000023C8EA,0x000000000023C8FA,0x000000000023C90A,0x000000000023C91A,0x000000000023C92A,0x000000000023C93A,0x000000000023C94A,0x000000000023C95A,0x000000000023C96A,0x000000000023C97A,0x000000000023C98A,0x000000000023C99A,0x000000000023C9AA,0x000000000023C9BA,0x000000000023C9CA,0x000000000023C9DA,0x000000000023C9EA,0x000000000023C9FA,0x000000000023CA0A,0x000000000023CA1A,0x000000000023CA2A,0x000000000023CA3A,0x000000000023CA4A,0x000000000023CA5A,0x000000000023CA6A,0x000000000023CA7A,0x000000000023CA8A,0x000000000023CA9A,0x000000000023CAAA,0x000000000023CABA,0x000000000023CACA,0x000000000023CADA,0x000000000023CAEA,0x000000000023CAFA,0x000000000023CB0A,0x000000000023CB1A,0x000000000023CB2A,0x000000000023CB3A,0x000000000023CB4A,0x000000000023CB5A,0x000000000023CB6A,0x000000000023CB7A,0x000000000023CB8A,0x000000000023CB9A,0x000000000023CBAA,0x000000000023CBBA,0x000000000023CBCA,0x000000000023CBDA,0x000000000023CBEA,0x000000000023CBFA,0x000000000023CC0A,0x000000000023CC1A,0x000000000023CC2A,0x000000000023CC3A,0x000000000023CC4A,0x000000000023CC5A,0x000000000023CC6A,0x000000000023CC7A,0x000000000023CC8A,0x000000000023CC9A,0x000000000023CCAA,0x000000000023CCBA,0x000000000023CCCA]
for i inrange(len(bp)): gdb.execute(f"pie b {bp[i]}") gdb.execute("pie b 0x23da2c") gdb.execute("pie run < inp.txt") gdb.execute("set $eax=0x0") gdb.execute("c") flag =b"" arch = gdb.selected_frame().architecture()for _ inrange(len(bp)):for i inrange(0xff): gdb.execute("si") current_pc =addr2num(gdb.selected_frame().read_register("pc")) disa = arch.disassemble(current_pc)[0]if(disa['asm']=='mov r10,0x100'): tmp = gdb.execute("x/2820i $pc", to_string=True) flag += self.get_flag(tmp)print(b"flag:"+ flag) gdb.execute("c")breakprint(flag)defaddr2num(addr):try:returnint(addr)# Python 3except:returnlong(addr)# Python 2SolverEquation()
Flag : COMPFEST15{sup3r_l0ng_fL46_5o_7hAT_YOu_w0nT_Be_a3le_t0_s0LvE_1t_m4nuALLy_w3ll_tecHnicaLly_u_c4n_bU7_s1mpL3_gdb_scRipt1n9_1s_aLL_You_n33D_a95dff5469}