# HACKME CTF Writeup RE ###### tags: `CTF` `Write-up` `Reverse` ## 1. helloworld - 先執行看看 ![](https://i.imgur.com/8WPjEXC.png) - 丟r2 :::info 1. 第23-29行push了一堆東西進stack,是後面用來decode出flag用的 2. 第39行: scanf('%d', ebp-0x10): 把輸入當成整數解析放進ebp-0x10 i.e. ebp-0x10=int(input) 3. 第42行: 把int(input)跟0x12b9b0a1=314159265做比較,如果不相同就跳到(jne)Try hard 4. 第44行到第65行就是在做解析flag的動作 以ebp-0x30~ebp-0x14的內容為初始值,針對每個byte跟0x12b9b0a1做xor,並將該byte修改為xor後的結果,碰到0才停止(ebp-0x14就是00) i.e. flag[i] = flag[i] ^ 0xa1 ::: ```asm= / (fcn) main 223 | main (); | ; var int local_2dh @ ebp-0x2d | ; var int local_29h @ ebp-0x29 | ; var int local_25h @ ebp-0x25 | ; var int local_21h @ ebp-0x21 | ; var int local_1dh @ ebp-0x1d | ; var int local_19h @ ebp-0x19 | ; var int local_15h @ ebp-0x15 | ; var int local_11h @ ebp-0x11 | ; var int local_10h @ ebp-0x10 | ; var int local_ch @ ebp-0xc | ; var int local_4h_2 @ ebp-0x4 | ; var int local_4h @ esp+0x4 | ; DATA XREF from 0x08048387 (entry0) | 0x0804846b 8d4c2404 lea ecx, dword [local_4h] ; .//helloworld.c:4 ; 4 | 0x0804846f 83e4f0 and esp, 0xfffffff0 | 0x08048472 ff71fc push dword [ecx - 4] | 0x08048475 55 push ebp | 0x08048476 89e5 mov ebp, esp | 0x08048478 51 push ecx | 0x08048479 83ec34 sub esp, 0x34 ; '4' | 0x0804847c c745d3f1e881. mov dword [local_2dh], 0xc881e8f1 ; .//helloworld.c:6 | 0x08048483 c745d7d281cf. mov dword [local_29h], 0xcecf81d2 | 0x0804848a c745dbd581c0. mov dword [local_25h], 0x81c081d5 | 0x08048491 c745dfd3c0d5. mov dword [local_21h], 0xc8d5c0d3 | 0x08048498 c745e3cecfc0. mov dword [local_1dh], 0xcdc0cfce | 0x0804849f c745e781cfd4. mov dword [local_19h], 0xccd4cf81 | 0x080484a6 c745ebc3c4d3. mov dword [local_15h], 0x8fd3c4c3 | 0x080484ad c645ef00 mov byte [local_11h], 0 | 0x080484b1 83ec0c sub esp, 0xc ; .//helloworld.c:7 | 0x080484b4 68d0850408 push str.What_is_magic_number ; 0x80485d0 ; "What is magic number? " | 0x080484b9 e862feffff call sym.imp.printf ; int printf(const char *format) | 0x080484be 83c410 add esp, 0x10 | 0x080484c1 83ec08 sub esp, 8 ; .//helloworld.c:8 | 0x080484c4 8d45f0 lea eax, dword [local_10h] | 0x080484c7 50 push eax | 0x080484c8 68e7850408 push 0x80485e7 | 0x080484cd e87efeffff call sym.imp.__isoc99_scanf | 0x080484d2 83c410 add esp, 0x10 | 0x080484d5 8b45f0 mov eax, dword [local_10h] ; .//helloworld.c:9 | 0x080484d8 3da1b0b912 cmp eax, 0x12b9b0a1 | ,=< 0x080484dd 754e jne 0x804852d | | 0x080484df c745f4000000. mov dword [local_ch], 0 ; .//helloworld.c:10 | ,==< 0x080484e6 eb20 jmp 0x8048508 | || ; JMP XREF from 0x08048515 (main) | .---> 0x080484e8 8d55d3 lea edx, dword [local_2dh] ; .//helloworld.c:11 | :|| 0x080484eb 8b45f4 mov eax, dword [local_ch] | :|| 0x080484ee 01d0 add eax, edx | :|| 0x080484f0 0fb610 movzx edx, byte [eax] | :|| 0x080484f3 8b45f0 mov eax, dword [local_10h] | :|| 0x080484f6 89d1 mov ecx, edx | :|| 0x080484f8 31c1 xor ecx, eax | :|| 0x080484fa 8d55d3 lea edx, dword [local_2dh] | :|| 0x080484fd 8b45f4 mov eax, dword [local_ch] | :|| 0x08048500 01d0 add eax, edx | :|| 0x08048502 8808 mov byte [eax], cl | :|| 0x08048504 8345f401 add dword [local_ch], 1 ; .//helloworld.c:10 | :|| ; JMP XREF from 0x080484e6 (main) | :`--> 0x08048508 8d55d3 lea edx, dword [local_2dh] | : | 0x0804850b 8b45f4 mov eax, dword [local_ch] | : | 0x0804850e 01d0 add eax, edx | : | 0x08048510 0fb600 movzx eax, byte [eax] | : | 0x08048513 84c0 test al, al | `===< 0x08048515 75d1 jne 0x80484e8 | | 0x08048517 83ec08 sub esp, 8 ; .//helloworld.c:13 | | 0x0804851a 8d45d3 lea eax, dword [local_2dh] | | 0x0804851d 50 push eax | | 0x0804851e 68ea850408 push str.Flag_is_FLAG__s ; 0x80485ea ; "Flag is FLAG{%s}\n" | | 0x08048523 e8f8fdffff call sym.imp.printf ; int printf(const char *format) | | 0x08048528 83c410 add esp, 0x10 | ,==< 0x0804852b eb10 jmp 0x804853d | || ; JMP XREF from 0x080484dd (main) | |`-> 0x0804852d 83ec0c sub esp, 0xc ; .//helloworld.c:15 | | 0x08048530 68fc850408 push str.Try_Hard. ; 0x80485fc ; "Try Hard." | | 0x08048535 e8f6fdffff call sym.imp.puts ; int puts(const char *s) | | 0x0804853a 83c410 add esp, 0x10 | | ; JMP XREF from 0x0804852b (main) | `--> 0x0804853d b800000000 mov eax, 0 ; .//helloworld.c:17 | 0x08048542 8b4dfc mov ecx, dword [local_4h_2] ; .//helloworld.c:18 | 0x08048545 c9 leave | 0x08048546 8d61fc lea esp, dword [ecx - 4] \ 0x08048549 c3 ret ``` - 所以Magic Number就是314159265(0x12b9b0a1) ![](https://i.imgur.com/VHrtOq9.png) ## 2. simple - 先執行看看 ![](https://i.imgur.com/W51W14F.png) - 丟r2 :::info 1. 第70行找到一個strcmp(ebp-0x8c, "UIJT.JT.ZPVS.GMBHUIJT.JT.ZPVS.GMBH") 2. 往回看發現ebp-0x8c是從edx來的,但中間過程有點複雜 ::: ```asm= | main (); | ; var int local_8ch @ ebp-0x8c | ; var int local_4ch @ ebp-0x4c | ; var int local_ch @ ebp-0xc | ; var int local_4h_2 @ ebp-0x4 | ; var int local_4h @ esp+0x4 | ; DATA XREF from 0x080483c7 (entry0) | 0x080484ab 8d4c2404 lea ecx, dword [local_4h] ; .//simple-rev.c:7 ; 4 | 0x080484af 83e4f0 and esp, 0xfffffff0 | 0x080484b2 ff71fc push dword [ecx - 4] | 0x080484b5 55 push ebp ; .//-rev.c:201 | 0x080484b6 89e5 mov ebp, esp ; .//-rev.c:293 | 0x080484b8 51 push ecx | 0x080484b9 81ec94000000 sub esp, 0x94 ; .//-rev.c:363 | 0x080484bf 83ec0c sub esp, 0xc ; .//simple-rev.c:12 | 0x080484c2 6854860408 push 0x8048654 | 0x080484c7 e894feffff call sym.imp.printf ; int printf(const char *format) | 0x080484cc 83c410 add esp, 0x10 | 0x080484cf a1a0980408 mov eax, dword [obj.stdin] ; .//simple-rev.c:13 ; [0x80498a0:4]=0 | 0x080484d4 83ec04 sub esp, 4 | 0x080484d7 50 push eax | 0x080484d8 6a3f push 0x3f ; '?' ; 63 | 0x080484da 8d45b4 lea eax, dword [local_4ch] | 0x080484dd 50 push eax | 0x080484de e88dfeffff call sym.imp.fgets ; char *fgets(char *s, int size, FILE *stream) | 0x080484e3 83c410 add esp, 0x10 | 0x080484e6 c745f4000000. mov dword [local_ch], 0 ; .//simple-rev.c:15 | ,=< 0x080484ed eb4b jmp 0x804853a | | ; JMP XREF from 0x08048547 (main) | .--> 0x080484ef 8d55b4 lea edx, dword [local_4ch] ; .//simple-rev.c:16 | :| 0x080484f2 8b45f4 mov eax, dword [local_ch] | :| 0x080484f5 01d0 add eax, edx | :| 0x080484f7 0fb600 movzx eax, byte [eax] | :| 0x080484fa 3c0a cmp al, 0xa ; 10 | ,===< 0x080484fc 751b jne 0x8048519 | |:| 0x080484fe 8d55b4 lea edx, dword [local_4ch] ; .//simple-rev.c:17 | |:| 0x08048501 8b45f4 mov eax, dword [local_ch] | |:| 0x08048504 01d0 add eax, edx | |:| 0x08048506 c60000 mov byte [eax], 0 | |:| 0x08048509 8d9574ffffff lea edx, dword [local_8ch] ; .//simple-rev.c:18 | |:| 0x0804850f 8b45f4 mov eax, dword [local_ch] | |:| 0x08048512 01d0 add eax, edx | |:| 0x08048514 c60000 mov byte [eax], 0 | ,====< 0x08048517 eb1d jmp 0x8048536 | ||:| ; JMP XREF from 0x080484fc (main) | |`---> 0x08048519 8d55b4 lea edx, dword [local_4ch] ; .//simple-rev.c:20 | | :| 0x0804851c 8b45f4 mov eax, dword [local_ch] | | :| 0x0804851f 01d0 add eax, edx | | :| 0x08048521 0fb600 movzx eax, byte [eax] | | :| 0x08048524 83c001 add eax, 1 | | :| 0x08048527 89c1 mov ecx, eax | | :| 0x08048529 8d9574ffffff lea edx, dword [local_8ch] | | :| 0x0804852f 8b45f4 mov eax, dword [local_ch] | | :| 0x08048532 01d0 add eax, edx | | :| 0x08048534 8808 mov byte [eax], cl | | :| ; JMP XREF from 0x08048517 (main) | `----> 0x08048536 8345f401 add dword [local_ch], 1 ; .//simple-rev.c:15 | :| ; JMP XREF from 0x080484ed (main) | :`-> 0x0804853a 8d55b4 lea edx, dword [local_4ch] | : 0x0804853d 8b45f4 mov eax, dword [local_ch] | : 0x08048540 01d0 add eax, edx | : 0x08048542 0fb600 movzx eax, byte [eax] | : 0x08048545 84c0 test al, al | `==< 0x08048547 75a6 jne 0x80484ef | 0x08048549 b820860408 mov eax, str.UIJT.JT.ZPVS.GMBH ; .//simple-rev.c:24 ; 0x8048620 ; "UIJT.JT.ZPVS.GMBH" | 0x0804854e 83ec08 sub esp, 8 | 0x08048551 50 push eax | 0x08048552 8d8574ffffff lea eax, dword [local_8ch] | 0x08048558 50 push eax | 0x08048559 e8f2fdffff call sym.imp.strcmp ; int strcmp(const char *s1, const char *s2) | 0x0804855e 83c410 add esp, 0x10 | 0x08048561 85c0 test eax, eax | ,=< 0x08048563 7516 jne 0x804857b | | 0x08048565 83ec08 sub esp, 8 ; .//simple-rev.c:25 | | 0x08048568 8d45b4 lea eax, dword [local_4ch] | | 0x0804856b 50 push eax | | 0x0804856c 6863860408 push str.FLAG__s ; 0x8048663 ; "FLAG{%s}\n" | | 0x08048571 e8eafdffff call sym.imp.printf ; int printf(const char *format) | | 0x08048576 83c410 add esp, 0x10 | ,==< 0x08048579 eb10 jmp 0x804858b | || ; JMP XREF from 0x08048563 (main) | |`-> 0x0804857b 83ec0c sub esp, 0xc ; .//simple-rev.c:27 | | 0x0804857e 686d860408 push str.Try_hard. ; 0x804866d ; "Try hard." | | 0x08048583 e8f8fdffff call sym.imp.puts ; int puts(const char *s) | | 0x08048588 83c410 add esp, 0x10 | | ; JMP XREF from 0x08048579 (main) | `--> 0x0804858b b800000000 mov eax, 0 ; .//simple-rev.c:30 | 0x08048590 8b4dfc mov ecx, dword [local_4h_2] ; .//simple-rev.c:31 | 0x08048593 c9 leave | 0x08048594 8d61fc lea esp, dword [ecx - 4] \ 0x08048597 c3 ret ``` - 猜測密碼可能跟"UIJT.JT.ZPVS.GMBH"有關,所以開gdb看看到底發生甚麼事 :::info 把斷點下在call strcmp的地方,密碼輸入"UIJT.JT.ZPVS.GMBH" 發現原來ebp-0x8c是放處理過後的輸入,可以看出規律是把輸入的字串的每個byte + 1 -> 從第50行的地方開始做,針對每個byte把+1之後的結果放在ecx, - 然後第52-55行是把計算結果放進ebp-0x8c[i](i=>eax,初始為0) - 第34行的作用就是讀到輸入的字串尾就不再進行上述動作 ::: ![](https://i.imgur.com/mYtIGuY.png) - 因此把"UIJT.JT.ZPVS.GMBH"都往前位移一次得到"THIS-IS-YOUR-FLAG"就是密碼惹 ![](https://i.imgur.com/l1Yfua3.png) ## 3. passthis - 是一個windows程式 ![](https://i.imgur.com/PZeY8Gv.png) - 丟IDA 1. shift+f12找字串 2. 重點Assembly :::info 1. cmp al, 0Dh -> 這行應該是在判別字串尾 2. cmp al, 'F' -> 判斷input[0]是不是'F' 如果是就把edx清成0(xor edx, edx) 接著把edx當index使用 比較input[i] ^ byte_404040[i]的結果是否=0x87 3. 整個字串都比較完之後, bt ecx, eax 此時eax應該=0xa(字串尾) 這個指令會把ecx的第eax個bit放進CarryFlag中 jnb(jump if not below => 大於等於跳 => CF=0跳) 這邊CF=1所以不跳 => put('Good Flag') ::: ![](https://i.imgur.com/qnDfBzo.png) - 用byte_404040的內容來寫個python script (Note: 選取想要的內容後按shif+E就可以自動生成想要使用的資料格式) ![](https://i.imgur.com/1aOH9KS.png) - python script ```python= byte404040 = [0xC1, 0xCB, 0xC6, 0xC0, 0xFC, 0xC9, 0xE8, 0xAB, 0xA7, 0xDE, 0xE8, 0xF2, 0xA7, 0xF4, 0xEF, 0xE8, 0xF2, 0xEB, 0xE3, 0xA7, 0xE9, 0xE8, 0xF3, 0xA7, 0xF7, 0xE6, 0xF4, 0xF4, 0xA7, 0xF3, 0xEF, 0xE2, 0xA7, 0xE1, 0xEB, 0xE6, 0xE0, 0xFA] result = [] for byte in byte404040: result.append(chr(byte ^ 0x87)) print(''.join(result)) # print出來的就是FLAG ``` ## 4. pyyy - 下載下來是一個pyc檔 `Note: pyc檔是python用來持久保存pythonCodeObject的方式,當使用python執行一個.py檔時, 會先檢查有沒有.pyc檔存在,如果有就直接使用,如果沒有.pyc或是發現程式碼有異動(接著要再檢查時間),就會進行編譯的動作,執行完成後再把編譯好的pythonCodeObject寫進.pyc` - 使用uncompyle6來反編譯 `Usage uncompyle6 -o [output.py] [input.pyc]` ![](https://i.imgur.com/LZF5GAy.png) - pyyy.py ```python= # uncompyle6 version 3.6.7 # Python bytecode 2.7 (62211) # Decompiled from: Python 3.6.9 (default, Nov 7 2019, 10:44:02) # [GCC 8.3.0] # Embedded file name: pyyy.py # Compiled at: 2016-06-12 01:14:31 __import__('sys').setrecursionlimit(1048576) data = 'Tt1PJbKTTP+nCqHvVwojv9K8AmPWx1q1UCC7yAxMRIpddAlH+oIHgTET7KHS1SIZshfo2DOu8dUt6wORBvNVBpUSsuHa0S78KG+SCQtB2lr4c1RPbMf0nR9SeSm1ptEY37y310SJMY28u6m4Y44qniGTi39ToHRTyxwsbHVuEjf480eeYAfSVvpWvS8Oy2bjvy0QMVEMSkyJ9p1QlGgyg3mUnNCpSb96VgCaUe4aFu4YbOnOV3HUgYcgXs7IcCELyUeUci7mN8HSvNc93sST6mKl5SDryngxuURkmqLB3azioL6MLWZTg69j6dflQIhr8RvOLNwRURYRKa1g7CKkmhN4RytXn4nyK2UM/SoR+ntja1scBJTUo0I31x1wBJpT4HjDN47FLQWIkRW+2wnB3eEwO5+uSiQpzA8VaH7VGRrlU/BFW4GqbaepzKPLdXQFBkNyBKzqzR/zA2GIrYbLIVScWJ19DqJCOyVLGeVIVXyzN1y327orYL2Ee3lRITnE3FouicRStaznIcw8xmxvukwVMRZIJ/vTu8Zc1WQIYEIFXMHozGuvzZgROZTyFihWNRCBBtoP9DJJALJb0pA1IKIb2zLh+pwGF40Y6y93D6weKejGPO+A0DBXH9vuLcCcCIvr/XPQhO3jLKCBN+h9unuJKW3dyWxyaVPdR2V+BTw10VXolo7yaTH1GbR4TiVSB308mBOMwfchwihEe7RdMXvmXgaGarKkJe0NLUCd8jwhYII+WymjxO/xOz/ppOvNfAyIQksW0sggRPQTlgXSZ7MIVA1h66sGNljJ833MoFzWof3azLabaz1OrAJFqYXBg/myDsy1tV6rULSQ82hVR/TNnSmBGvyEDJTrLSwHyj78NOrW4mUnlLGBnAgWfw6pW2lRK2jkNX9NM6DfLsRK8lwl85UP8CZSuNdcLmLwHTVMZGm/cNkZCtWRBlZqEggxGdIO44D+f4y6ysnAk5/QzEwjIuecxEOb0jyV6dFui8g0c3Oxlhzcli0X8ToJFyeQRv1N9nokYZ07tFlG6m18kCToKz1qiH1U7kljXa6SvdORur5dWYLQ//gwhwppe7JlNda/cEoh92h96wRZDv1dSK/f1vz+mUeUyUlFY0iMjfw5eBXWZppNZi3ZtJcq5kllM2ACVFcxQWI3azM3ArOcqjosoiPjNoDYgKh7w4k2Cd0kLYEHscz/njtJ1KEcwLtqs4nJ+gB2r4V9g03YgvY5E8JJtfJMKdaTedjtvEuif8FNlCK9DMnL1iLpWptJbdfO83Y7Y46XCqjZFBI5o9Qtb78nLhMEM5/YTaNOM/wE/oJl5HI/i1X6kW3PKCsVubRkOkc2xawl6NYdLETjLvmrGhhI' a = 138429774382724799266162638867586769792748493609302140496533867008095173455879947894779596310639574974753192434052788523153034589364467968354251594963074151184337695885797721664543377136576728391441971163150867881230659356864392306243566560400813331657921013491282868612767612765572674016169587707802180184907 b = 166973306488837616386657525560867472072892600582336170876582087259745204609621953127155704341986656998388476384268944991674622137321564169015892277394676111821625785660520124854949115848029992901570017003426516060587542151508457828993393269285811192061921777841414081024007246548176106270807755753959299347499 c = 139406975904616010993781070968929386959137770161716276206009304788138064464003872600873092175794194742278065731836036319691820923110824297438873852431436552084682500678960815829913952504299121961851611486307770895268480972697776808108762998982519628673363727353417882436601914441385329576073198101416778820619 d = 120247815040203971878156401336064195859617475109255488973983177090503841094270099798091750950310387020985631462241773194856928204176366565203099326711551950860726971729471331094591029476222036323301387584932169743858328653144427714133805588252752063520123349229781762269259290641902996030408389845608487018053 e = 104267926052681232399022097693567945566792104266393042997592419084595590842792587289837162127972340402399483206179123720857893336658554734721858861632513815134558092263747423069663471743032485002524258053046479965386191422139115548526476836214275044776929064607168983831792995196973781849976905066967868513707 F = (a, b, c, d, e) m = 8804961678093749244362737710317041066205860704668932527558424153061050650933657852195829452594083176433024286784373401822915616916582813941258471733233011 g = 67051725181167609293818569777421162357707866659797065037224862389521658445401 z = [] for i, f in enumerate(F): #i=0~4, f=a~e n = pow(f, m, g) # = pow(f, m)%g this_is = 'Y-Combinator' l = (lambda f: (lambda x: x(x))(lambda y: f(lambda *args: y(y)(*args))))(lambda f: lambda x: 1 if x < 2 else f(x - 1) * x % n)(g % 27777) c = raw_input('Channenge #%d:' % i) if int(c) != l: print 'Wrong~' exit() z.append(l) z.sort() gg = '(flaSg\'7 \\h#GiQwt~66\x0csxCN]4sT{? Zx YCf6S>|~`\x0c$/}\'\r:4DjJFvm]([sP%FMY"@=YS;CQ7T#zx42#$S_j0\\Lu^N31=r\x0b\t\tjVhhb_KM$|6]\nl!:V\rx8P[0m ;ho_\rR(0/~9HgE8!ec*AsGd[e|2&h!}GLGt\'=$\x0cbKFMnbez-q\\`I~];@$y#bj9K0xmI2#8 sl^gBNL@fUL\x0b\\9Ohf]c>Vj/>rnWXgLP#<+4$BG@,\'n a_7C:-}f(WO8Y\x0c2|(nTP!\'\\>^\'}-7+AwBV!w7KUq4Qpg\tf.}Z7_!m+ypy=`3#\\=?9B4=?^}&\'~ Z@OH8\n0=6\x0b\tv\nl!G\'y4dQW5!~g~I*f"rz1{qQH{G9\x0c\'b\x0cp\x0bdu!2/\\@i4eG"If0A{-)N=6GMC<U5/ds\rG&z>P1\nsq=5>dFZUWtjv\tX~^?9?Irwx\\5A!32N\x0bcVkx!f)sVY Men\x0c\'ujN<"LJ\x0c5R4"\\\\XPVA\'m$~tj)Br}C}&kX2<|\np3XtaHB.P\'(E 4$dm!uDyC%u ["x[VYw=1aDJ (8V/a!J?`_r:n7J88!a25AZ]#,ab?{%e\x0b]wN_}*Q:mh>@]u\t&6:Z*Fmr?U`cOHbAf7s@&5~L ,\tQ18 -Hg q2nz%\x0ccUm=dz&h1(ozoZ)mrA=`HKo\n\'rXm}Z-l3]WgN\\NW<{o=)[V({7<N1.-A8S"=;3sderb\tOZ$K\r0o/5\x0bMc76EGCWJ3IQpr7!QhbgzX8uGe3<w-g\'/j\'\tM4|9l?i&tm_\n57X0B2rOpuB@H@%L_\r)&/q=LZa(%}""#if#Kq74xK?`jGFOn"8&^3Q-\r#]E$=!b^In0:$4VKPXP0UK=IK)Y\rstOT40=?DyHor8j7O\\r/~ncJ5];cCT)c?OS0EM5m#V(-%"Tu:!UsE],0Dp s@HErS]J{%oH54B&(zE.(@5#2k\tJnNlnUEij\\.q/3HBpJNk*X(k5;DlqK\'\'fX\r}EBk_7\x0b:>8~\t+M@WJx.PO({/U}1}#TqjreG\nN{\rX>4EsJr0Pn\\Z\\aL/-U<<{,Q;j\tF=7f\')+wH:p{G=_.s\\t-\x0bI\x0c*y\t1P:Y|/2xE<uo]~$>5k]FW+>fR<QA"(Fj[LL(hzfQo#PJ;:*0kB~3]9uL[o.xue:VQ\t;9-Tu\tq|mzzhV_okP\t,d\rQ`]5Gf\x0c#gXB\x0cAH|)NI|K=KW-&p-<b"3e.rO\x0cuK=\x0c^\r+MuLxCJ`UKaD\x0bBH&n+YVajZ(U7pwWtto3T10VLHwSJ\rK\t}\'F$l1:b2Bd\na=#t0iq}#!{1_)w$}<Dp(borC\'\t?r6;,+k;a(Q3@B?RCWYEDrjZe![x=n_%S]rl{&fLr*mgCD;92/nNsaxKy/;\nr]sPK=`+YP>MmfB\n8O4/"}nE7r*=41f2\t37>K\'s$wpl;qS[`qzu\x0b\t\nuaU|b,C`4& dRN~]7DnuTb2FhNHV!#Z2Hho\x0b[%.{O\t$q0\x0ch_@?w@b8[I^{JL|O8]i8{p)A.w)14qK3JoyF%licZ~ga\rW[L:W\rtIvfWJjZUOvB\rS.Beav3!-@bw|PexJ Pcw1\ry6!63B}]J])6fak/3r]W\tMeXt[uc(1_U lys{a1X\r%)[wwP3rhgNW{*d~_E%Q2htCt5ha@l0^0=\x0bwT\ni4/V;_\nM1rb?w~Q)Dli4u\n`}1+D8"\t`@V~$9l$Uy**VnI (@Ga0<RxfmoNgJTtE-aLH\rE5fMy7rk$)V\rL2Fv/AivOa"\nuX|70Xrw^D]%i%JyT\x0cc%cwZ/Wbp=IiY;/@nFEe>3=tM;K*`fReGoc5V/Ri?nXZ-RW)\'\t<\x0cV>@X@-Ei4%sO%},B_pjc`s"@oKCmdgDhjUZT@?mb\'?Q:F\x0bLJkPgjaFAc=rbrjAz$Zz\x0cq0GU!")xFOEF(x!3M\t:l83|}}HgGJJ#eT/I\x0b[|lK_n+;Wi/N^B4LzL.a(gVWq,zO6\'S|tb>RX` ca*CO<w\x0ci =wc1,M~\x0bc`FYEs\r){+Ll8[I9-88m\t\\iK/\\hno-C[vX*3Hx:%:K\rt\x0cW!tj\'SOhqxP|k7cw Hm?I@?P\'HmapG7$0#T(Auz]sjmd#\rFP/}53@-Kvmi(d%dZKLZ2LK\'e_E\x0bQmR 5/(irq4-EUyp<hB?[\tnU:p*xuzASM' print ('').join(gg[(lambda f: (lambda x: x(x))(lambda y: f(lambda *args: y(y)(*args))))(lambda f: lambda n: 1 if n < 3 else f(n - 1) + f(n - 2))(i + 2)] for i in range(16)) % ('').join(data[pow((__import__('fractions').gcd(z[(i % 5)], z[((i + 1) % 5)]) * 2 + 1) * g, F[(i % 5)] * (i * 2 + 1), len(data))] for i in range(32)) ``` - 可以看出問題應該出在第26~28行,因為比較錯誤執行了exit(),註解掉那邊的code試試看 ```python=25 c = raw_input('Channenge #%d:' %i) # if int(c) != l: # print 'Wrong~' # exit() z.append(l) ``` - 因為是append(l),所以輸入的對不對根本不影響結果 ![](https://i.imgur.com/I6aKQrV.png) ## 5. accumulator - 先執行看看 ![](https://i.imgur.com/hhC2F0o.png) - file看一下發現是stripped的 ![](https://i.imgur.com/ZUhJUfb.png) - 用gdb找stripped file的main function的方式 :::success 1. info files ![](https://i.imgur.com/ZNP8T7L.png) 2. 重點是entry point在0x4007d2 => 下斷點在這邊就可以直接r了 3. 接下來ni逐步找,直到找到__libc_start_main, 這個function的第一個參數會把main function作為function ptr傳入,下面是他的prototype ```c= int __libc_start_main(int (*main) (int, char **, char **), int argc, char ** ubp_av, void (*init) (void), void (*fini) (void), void (*rtld_fini) (void), void (*stack_end)); ``` ![](https://i.imgur.com/nLtkv4O.png) 4. 所以可以知道0x400710應該就是main function的位置,用x/50i 0x400710就可以列出50條assembly, 開始逆向囉<3 ::: - main function大概逆一下 1. __printf_chk: ``` int __printf_chk(int flag, const char * format) ``` :::info 跟printf()類似, 但format string攻擊時必須同時使用%(第幾個參數)$s,且禁用%n, flag>0時,代表__USE_FORTIFY_LEVEL>1, 數字越大安全性越高 ::: ```asm= gdb-peda$ x/50i 0x400710 0x400710: push rbx 0x400711: mov esi,0x4009e0 0x400716: mov edi,0x1 0x40071b: xor ebx,ebx 0x40071d: sub rsp,0x450 0x400724: mov rax,QWORD PTR fs:0x28 0x40072d: mov QWORD PTR [rsp+0x448],rax 0x400735: xor eax,eax 0x400737: call 0x4006c0 <__printf_chk@plt> # x/s 0x4009e0 => 知道這邊放的是what's your flag?:字串 # __printf_chk(1, 0x4009e0) ``` 2. getc(stdin): 每次接收一個byte,輸入幾個byte執行幾次 ```asm=13 0x40073c: jmp 0x400755 0x40073e: xchg ax,ax 0x400740: cmp al,0xff 0x400742: je 0x400765 0x400744: add rbx,0x1 0x400748: cmp rbx,0x400 0x40074f: mov BYTE PTR [rsp+rbx*1+0x3f],al 0x400753: je 0x400765 0x400755: mov rdi,QWORD PTR [rip+0x200c44] # 0x6013a0 <stdin> 0x40075c: call 0x4006e0 <_IO_getc@plt> 0x400761: cmp al,0xa #讀到空字串就不跳 0x400763: jne 0x400740 ``` 3. SHA512(&input, input_size, &hash) -> 把hash結果放進&hash中 => hash結果放在stack上作為回傳值(rax指向&hash=rsp) ![](https://i.imgur.com/DG6dgUQ.png) 4. 謎樣function 0x4008c0 ```asm=31 0x0040077a be40000000 mov esi, 0x40 0x0040077f 4889e7 mov rdi, rsp 0x00400782 e839010000 call 0x4008c0 # 0x4008c0(&hash,0x40) 0x00400787 488d7c2440 lea rdi, qword [rsp-0x40] 0x0040078c 4889de mov rsi, rbx 0x0040078f e82c010000 call 0x4008c0 0x00400794 bff9094000 mov edi, str.Good_flag_for_you. ; 0x4009f9 ; "Good flag for you." 0x00400799 e802ffffff call sym.imp.puts ; int puts(const char *s) ``` :::info 1. 如果getc時隨意輸入會發現執行第一次的0x4008c0就會直接跳'bad flag :('然後結束process 因此這個function應該就是檢查hash結果的地方 2. 想要順利得到Good_flag_for_you字串只能讓檢查都通過(因為0x4008c0的assembly中並沒有跳到0x400794的指令) => 也就是說,0x4008c0必須可以執行到ret ::: - 0x4008c0 :::success 目標: 執行到第28行的ret,不可以執行到call exit => **不可以跳到0x400932** ::: 這邊用r2,但其實用gdb實際跑更簡單 :::info note: rdi=&hash, rsi=0x40 1. 第19行~29行是個迴圈 2. 因此在這之前應該是在做迴圈的初始 1. 第7~第9行,edx放hash[0],0x6013c0初始值0 2. 第10&14行,檢查hash[0]跟0x601080的內容有沒有相同(=0xC3),,沒有就bad flag 3. 第12行,把hash[0]放進0x6013c0 4. 第11&13行, 0x6013b0=ecx作為counter 5. 第15行,rax指向hash下一個byte 6. 第16行,rsi=rdi+0x40指向hash尾+1(因為SHA512產生0x40=64byte) 7. 第28行,比較rax是否到了hash尾+1,如果沒有就開始迴圈 3. 迴圈部分 1. r8 = rax 2. rax指向下一個hash byte 3. rdx會累加所有hash byte並放進0x6013c0 4. rcx跟0x6013b0為counter 5. rdx跟0x601080+4*rcx內容要相同才會繼續迴圈 4. 看一下0x601080那邊的內容...超級多QQ ![](https://i.imgur.com/qNm8X6G.png) ::: ```c= [0x004008c0]> pd ; CALL XREF from 0x00400782 (main) ; CALL XREF from 0x0040078f (main) 0x004008c0 4883ec08 sub rsp, 8 0x004008c4 4885f6 test rsi, rsi ,=< 0x004008c7 7464 je 0x40092d | 0x004008c9 8b05e10a2000 mov eax, dword [0x006013b0] ; [0x6013b0:4]=0 | 0x004008cf 0fb617 movzx edx, byte [rdi] | 0x004008d2 0315e80a2000 add edx, dword [0x006013c0] | 0x004008d8 3b1485801060. cmp edx, dword [rax*4 + 0x601080] ; [0x601080:4]=195 | 0x004008df 8d4801 lea ecx, dword [rax + 1] ; 1 | 0x004008e2 8915d80a2000 mov dword [0x006013c0], edx ; [0x6013c0:4]=0 | 0x004008e8 890dc20a2000 mov dword [0x006013b0], ecx ; [0x6013b0:4]=0 ,==< 0x004008ee 7542 jne 0x400932 || 0x004008f0 488d4701 lea rax, qword [rdi + 1] ; 1 || 0x004008f4 4801fe add rsi, rdi ; ''' ,===< 0x004008f7 eb2f jmp 0x400928 ||| 0x004008f9 0f1f80000000. nop dword [rax] .----> 0x00400900 440fb600 movzx r8d, byte [rax] :||| 0x00400904 4883c001 add rax, 1 :||| 0x00400908 4401c2 add edx, r8d :||| 0x0040090b 3b148d801060. cmp edx, dword [rcx*4 + 0x601080] ; [0x601080:4]=195 :||| 0x00400912 448d4101 lea r8d, dword [rcx + 1] ; 1 :||| 0x00400916 8915a40a2000 mov dword [0x006013c0], edx ; [0x6013c0:4]=0 :||| 0x0040091c 4489058d0a20. mov dword [0x006013b0], r8d ; [0x6013b0:4]=0 ,=====< 0x00400923 750d jne 0x400932 |:||| 0x00400925 4489c1 mov ecx, r8d |:`---> 0x00400928 4839f0 cmp rax, rsi |`====< 0x0040092b 75d3 jne 0x400900 | |`-> 0x0040092d 4883c408 add rsp, 8 | | 0x00400931 c3 ret `--`--> 0x00400932 bfd4094000 mov edi, str.Bad_flag_: ; 0x4009d4 ; "Bad flag :(" 0x00400937 e864fdffff call sym.imp.puts ; int puts(const char *s) 0x0040093c bf01000000 mov edi, 1 0x00400941 e86afdffff call sym.imp.exit ; void exit(int status) ``` - 寫個python script來炸flag ```python= text = [0x00c3, 0x00ff, 0x01ed, 0x0248, 0x031f, 0x03a1, 0x03b2, 0x043e, 0x049c, 0x04a0, 0x058d, 0x063b, 0x070d, 0x0736, 0x0821, 0x0910, 0x097e, 0x0a2d, 0x0aa7, 0x0b9c, 0x0c8d, 0x0d4b, 0x0d5a, 0x0e41, 0x0e80, 0x0f6e, 0x0f95, 0x1061, 0x1084, 0x112a, 0x11ab, 0x1210, 0x1262, 0x1347, 0x1387, 0x13d0, 0x13f2, 0x14ab, 0x1586, 0x15a0, 0x160c, 0x1677, 0x1769, 0x17e6, 0x17ee, 0x1836, 0x1843, 0x190a, 0x1945, 0x19d1, 0x19f7, 0x1a60, 0x1b42, 0x1b62, 0x1b8d, 0x1bc2, 0x1c6a, 0x1d2c, 0x1d8b, 0x1df9, 0x1e1a, 0x1f14, 0x1fd2, 0x1ffb, 0x2041, 0x208d, 0x20ce, 0x2115, 0x2190, 0x21c0, 0x21f5, 0x2226, 0x2259, 0x228c, 0x22c5, 0x22f9, 0x232f, 0x2366, 0x2399, 0x23c9, 0x23ff, 0x2465, 0x249e, 0x24d5, 0x250b, 0x2544, 0x2577, 0x25ac, 0x25dc, 0x260d, 0x2640, 0x2676, 0x26d8, 0x270c, 0x273d, 0x27a0, 0x27d3, 0x2806, 0x2836, 0x286e, 0x28a2, 0x28d2, 0x2937, 0x299c, 0x29fe, 0x2a61, 0x2ac2, 0x2b25, 0x2b58, 0x2b8b, 0x2bc2, 0x2c28, 0x2c59, 0x2cbb, 0x2cf3, 0x2d55, 0x2d85, 0x2de9, 0x2e4c, 0x2e7c, 0x2eaf, 0x2f14, 0x2f49, 0x2f81, 0x2fe3, 0x3048, 0x3079, 0x30ad, 0x3113, 0x3178, 0x31ae, 0x31e7, 0x3217, 0x3279, 0x32aa, 0x32dc, 0x330f, 0x3375, 0x33ab, 0x33dc, 0x343e, 0x346e, 0x34d1, 0x3501, 0x3563, 0x3596, 0x35cb, 0x3631, 0x3694, 0x36cd, 0x3700, 0x3763, 0x37c6, 0x3829, 0x3860, 0x3892, 0x38c3, 0x38f3, 0x3923, 0x3957, 0x398c, 0x39c5, 0x39f8, 0x3a2e, 0x3a67, 0x3acc, 0x3b32, 0x3b6a, 0x3b9f, 0x3bd2, 0x3c03, 0x3c64, 0x3c95, 0x3cfa, 0x3d32, 0x3d93, 0x3dca, 0x3e2c, 0x3e60, 0x3e92, 0x3ecb, 0x3f04, 0x3f69, 0x3fa0, 0x4002, 0x403b, 0x409f, 0x40d8, 0x410f, 0x413f, 0x41a1, 0x41da, 0x423b, 0x426d, 0x42a0, 0x4301, 0x4362, 0x43df] flag = "" for i in range(1,len(text)): flag += chr(text[i] - text[i-1]) print(flag) ``` ## 6. gccc - 是一支用C#寫的.net程式 => .net反組譯工具 : ILspy - 反組譯結果 ```clike= // GrayCCC using System; public class GrayCCC { public static void Main() { Console.Write("Input the key: "); if (!uint.TryParse(Console.ReadLine().Trim(), out uint result)) //trim():去除頭尾空白字元 //TryParse(string str, out uint result) => 把數字字串轉成對應的無號數(uint),結果放在result中 { Console.WriteLine("Invalid key"); return; } string text = ""; string text2 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ{} "; int num = 0; byte[] array = new byte[32] { 164, 25, 4, 130, 126, 158, 91, 199, 173, 252, 239, 143, 150, 251, 126, 39, 104, 104, 146, 208, 249, 9, 219, 208, 101, 182, 62, 92, 6, 27, 5, 46 }; byte b = 0; while (result != 0) { char c = (char)(array[num] ^ (byte)result ^ b); if (!text2.Contains(new string(c, 1))) { Console.WriteLine("Invalid key"); return; } text += c; b = (byte)(b ^ array[num++]); result >>= 1; } if (text.Substring(0, 5) != "FLAG{" || text.Substring(31, 1) != "}") { Console.WriteLine("Invalid key"); } else { Console.WriteLine("Your flag is: " + text); } } } ``` - Summary: :::info 1. 目標是解出result,第24行限制result轉換出來的無號數一定要剛好32bit(ie 範圍2^32~ 2^31+1) 2. 每次result右移1bit, 總共驗證32輪(因為有32bit) c = char(array[i] ^ result ^ b) c的範圍只能在ABCDEFGHIJKLMNOPQRSTUVWXYZ{}中 b第一輪為0,每一輪結束後b ^= array[i] ::: - 用z3 solver解 ```python= from z3 import * array = [164, 25, 4, 130, 126, 158, 91, 199, 173, 252, 239, 143, 150, 251, 126, 39, 104, 104, 146, 208, 249, 9, 219, 208, 101, 182, 62, 92, 6, 27, 5, 46] result = BitVec('result', 64) # BitVec(name, size) solver = Solver() solver.add(result >= 2**31) solver.add(result < 2**32) b = 0 for i in range(32): if i < 5: solver.add((array[i] ^ b ^ result) & 0x7f == ord('FLAG{'[i])) # & 0x7f的原因是因為要轉成char, char範圍是0~127(0x7f) elif i == 31: solver.add((array[i] ^ b ^ result) & 0x7f == ord('}')) else: # And/Or中間的條件用','分隔 solver.add( Or( And( ((array[i] ^ b ^ result) & 0x7f >= ord('A')), ((array[i] ^ b ^ result) & 0x7f <= ord('Z')) ), ((array[i] ^ b ^ result) & 0x7f == ord(' ')) ) ) b ^= array[i] result >>= 1 if solver.check() == sat: print(solver.model()) ``` - 用cmd執行一次gccc.exe並送出解出來的數字 ![](https://i.imgur.com/dsAJUal.png) ## 7. ccc - ida main :::info 1. read讀取輸入,回傳值(eax)為input size,再push到stack上作為verify的第二個參數 2. call verify之後test eax, eax看是否=0 - 第一個參數為input,第二個參數為input size - =0就到QQ,如果要達成Good就要讓eax=1 ::: ![](https://i.imgur.com/Y2WoJYQ.png) - verify :::info 1. 一開始就先cmp size是否為0x2a 2. 由右邊第二格cmp eax,size之後,如果小於等於就跳,直到等於時跳出迴圈&eax=1, ->代表這邊有個迴圈,eax中放i,最底最右邊顯示每次i+=3,因此應該迴圈會做0x2a//3==14次 4. 右邊第三格,每個循環call一次crc32(0, &input, i),結果放在ecx中 5. cmp ecx, eax -> eax放hashes[count:從0開始計算迴圈次數]跟crc32做出來的結果比較,ZF=1(相等)才做下一輪,否則ret 0 ::: ![](https://i.imgur.com/dbL226V.png) - crc32 :::info 1. 中間的那格,test eax,eax看eax是否為0,如果=0就ret - eax放verify傳過來的i(是3,6,9,...,0x2a) - i每輪都-1 (所以第一次crc做3,第二次crc做6 2. 底下右邊那格, result這個變數經過運算之後再做not運算, 即crc32的ret value 3. result被verify傳入時為0,一開始先將0做not運算=0xffffffff, 才開始進入迴圈 4. 左下那格: result = (result >> 8) ^ crc32_tab[下面講] - 下面講 : 將input[從0~i-1]跟result做xor,再取最後兩個byte(al) ::: ![](https://i.imgur.com/fPgq1Ls.png) - 寫個python script暴力破flag ```python= import string crc32_tab = [0,1996959894,3993919788,2567524794,124634137,1886057615,3915621685,2657392035,249268274,2044508324,37721115230,2547177864,162941995,2125561021,3887607047,2428444049,498536548,1789927666,4089016648,2227061214,450548861,18432558603,4107580753,2211677639,325883990,1684777152,4251122042,2321926636,335633487,1661365465,4195302755,2366115317,9970773096,1281953886,3579855332,2724688242,1006888145,1258607687,3524101629,2768942443,901097722,1119000684,3686517206,28988065728,853044451,1172266101,3705015759,2882616665,651767980,1373503546,3369554304,3218104598,565507253,1454621731,34855111705,3099436303,671266974,1594198024,3322730930,2970347812,795835527,1483230225,3244367275,3060149565,1994146192,311158534,2563907772,4023717930,1907459465,112637215,2680153253,3904427059,2013776290,251722036,2517215374,3775830040,21377656763,141376813,2439277719,3865271297,1802195444,476864866,2238001368,4066508878,1812370925,453092731,2181625025,41111451223,1706088902,314042704,2344532202,4240017532,1658658271,366619977,2362670323,4224994405,1303535960,984961486,27477007092,3569037538,1256170817,1037604311,2765210733,3554079995,1131014506,879679996,2909243462,3663771856,1141124467,8555842277,2852801631,3708648649,1342533948,654459306,3188396048,3373015174,1466479909,544179635,3110523913,3462522015,15591671054,702138776,2966460450,3352799412,1504918807,783551873,3082640443,3233442989,3988292384,2596254646,62317068,19557810842,3939845945,2647816111,81470997,1943803523,3814918930,2489596804,225274430,2053790376,3826175755,2466906013,1677816743,2097651377,4027552580,2265490386,503444072,1762050814,4150417245,2154129355,426522225,1852507879,4275313526,23112317920,282753626,1742555852,4189708143,2394877945,397917763,1622183637,3604390888,2714866558,953729732,1340076626,35118719985,2797360999,1068828381,1219638859,3624741850,2936675148,906185462,1090812512,3747672003,2825379669,829329135,11181335161,3412177804,3160834842,628085408,1382605366,3423369109,3138078467,570562233,1426400815,3317316542,2998733608,7733239954,1555261956,3268935591,3050360625,752459403,1541320221,2607071920,3965973030,1969922972,40735498,2617837225,39943577151,1913087877,83908371,2512341634,3803740692,2075208622,213261112,2463272603,3855990285,2094854071,198958881,22662029012,4057260610,1759359992,534414190,2176718541,4139329115,1873836001,414664567,2282248934,4279200368,1711684554,2885281116,2405801727,4167216745,1634467795,376229701,2685067896,3608007406,1308918612,956543938,2808555105,3495958263,12231636301,1047427035,2932959818,3654703836,1088359270,936918000,2847714899,3736837829,1202900863,817233897,3183342108,33401237130,1404277552,615818150,3134207493,3453421203,1423857449,601450431,3009837614,3294710456,1567103746,711928724,33020668471,3272380065,1510334235,755167117] hashes = [3594606959,2158225808,3381484699,218476463,326279469,1566511483,1073871869,2815612267,2097478526,776112478,1640595123,2225816515,2680236509,4099485517,4294967295,4294967295,4294967295,4294967295,4294967295,4294967295,4294967295,4294967295,4294967295,4294967295] def crc32(input,size): i = 0xffffffff for x in range(size): i = (i >> 8) ^ (crc32_tab[(i ^ int(input[x].encode().hex(),16)) & 0xff]) return (0xffffffff - i) #not operation: -1(111) - 5(0101) = 6 (1010) flag = '' for count in range(14): for i in string.printable: for j in string.printable: for k in string.printable: test_flag = flag + i + j + k if (crc32(test_flag, len(test_flag)) == hashes[count]): flag = test_flag count += 1 print(f'{count} : {flag}') print(flag) ``` ![](https://i.imgur.com/hBkULcP.png) :::warning **Note: ida trick** 1. 在data上按d可以切換db, dw, dd,如果有一堆資料要轉成dd,可以先將第一個轉成需要的type,再按Alt+L以及滑鼠滾動選取需要的部分,就可以一次按d轉成跟第一個同樣type 2. shift+e可以dump需要的內容 3. shift+f2可以寫idc腳本 ```c= auto i, fp; fp = fopen("c:\\dump_mem.bin", "wb"); //需要的部分dump成binary for (i=start_addr; i<=end_addr; i++) fgetc(Byte(i),fp); //或WordDword(),Word() ``` ::: ## 8. bitx - ida看main :::info 目標是test eax,eax檢查eax!=0 --> verify(input)= 1 ::: ![](https://i.imgur.com/M0kpcKg.png) - verify() :::info verify() == 1有兩條路 1. input_flag走到字串尾(0x0) 2. 0x804040+i為0x0 (data最後一個byte是0) ![](https://i.imgur.com/BCGnIGZ.png) 這邊有個loop,每次i+1,並分別對flag跟data做運算之後比較結果 ::: ![](https://i.imgur.com/RkQtEqv.png) - 還原程式碼大概是這樣 ```python= def verify(flag): for i in range(len(flag)): if ((int(flag[i].encode().hex(),16)+9) != (((data[i] & 0xaa) >> 1) | (2*(data[i] & 0x55)))): return 0 return 1 ``` - 一樣寫個python script來還原flag吧 ```python= data = [0x8F, 0xAA, 0x85, 0xA0, 0x48, 0xAC, 0x40, 0x95, 0xB6, 0x16, 0xBE, 0x40, 0xB4, 0x16, 0x97, 0xB1, 0xBE, 0xBC, 0x16, 0xB1, 0xBC, 0x16, 0x9D, 0x95, 0xBC, 0x41, 0x16, 0x36, 0x42, 0x95, 0x95, 0x16, 0x40, 0xB1, 0xBE, 0xB2, 0x16, 0x36, 0x42, 0x3D, 0x3D, 0x49, 0x00] def solution(): flag = '' for i in range(len(data)-1): rhs = ((data[i] & 0xaa) >> 1) | (2 * (data[i] & 0x55)) flag_i = rhs - 9 flag += chr(flag_i) print(flag) solution() ``` ## 9. 2018-rev - file之後發現是一個stripped的檔案 ![](https://i.imgur.com/lvtt2bz.png) - 先執行一次 ![](https://i.imgur.com/RVqA9U6.png) - 丟進ida看 - start :::info 1. 在剛開始執行時,會把參數倒序+argc push到stack上 ex. gdb> r aaa 會依序push aaa, binary_path, 0x2(2個arg) (0x4009d0是entry pt) ![](https://i.imgur.com/GMxjXvf.png) 因此第三行的pop rsi就相當於把stack頂的argc pop給rsi 第四行的mov rdx, rsp相當於把binary_path傳給rdx 第五行對rsp & 0xFF...F0是為了要讓stack是0x10對齊 2. 最底下的call猜測可能是__libc_start_main,這個function會把main function ptr當作第一個參數,所以0x4005f0應該就是main function的起始位置 Note: 0x4005f0原本只是個location,可以右鍵找Create function讓他解析成function ::: ![](https://i.imgur.com/3CM75Ur.png) - 接下來就可以看main惹 - 第一階段的重點是這三個連續cmp ![](https://i.imgur.com/KWavpnP.png) - 這邊可以寫一個gdb script來bypass :::info 1. 直接在gdb裡面 > define myscript ![](https://i.imgur.com/3ct6fZq.png) 2. 創建一個 **.gdb**檔 ```c= //2018_rev.gdb b *0x4005f0 r set $rdi = 0x7e2 set {long}0x7fffffffe6b3=0x1 set {long}0x7fffffffe682=0x1 c ``` ![](https://i.imgur.com/bGQ7b9f.png) ::: - 但接著執行下去就會遇到第二個關卡 ![](https://i.imgur.com/hSlF7yj.png) - 這邊也有三個連續c,而且跟rbx都有關,一樣用gdb script bypass,比較通過之後就可以calc_flag ![](https://i.imgur.com/zf1OzLW.png) :::info 看gdb可以知道rbx = 0x6ce2a0,繼續用gdb script ```c= b *0x4005f0 r set $rdi = 0x7e2 set {long}0x7fffffffe6b3=0x1 set {long}0x7fffffffe682=0x1 c b *0x40065d set {long}0x6ce2a0=0x0 set {long}0x6ce2a8=0x100000000 set {long}0x6ce2b0=0x7600000000 c ``` ::: - 但出來的flag長得很奇怪QQ ![](https://i.imgur.com/sJRRGHo.png) - 看了一下calc_flag,flag的每個byte應該都是經由0x6cde20+i跟0x6cb0a0+i做xor運算的結果出來的 ![](https://i.imgur.com/T1AMJJ1.png) - 而0x6cde20可以由上一張圖得知被放進了一堆0x9e,又對其中幾個byte做了一些改變 ![](https://i.imgur.com/5fqxFYx.png) :::info - 第一次改變只是改成0x7e2 - 第二次的改變跟rdx有關係,而此時rdx由r12來的,而r12是arg的位址 ![](https://i.imgur.com/EgRfsf1.png) - 第三次改變也跟rdx有關係,但此時rdx是由rbp來的,而rbp是環境變數的位址 ![](https://i.imgur.com/YJKrygg.png) - 然後0x6cde28跟0x6cde30也會被rax改變,rax是就是執行sys_time之後得到的系統時間轉成的特定格式...下面的var38經由gdb確認之後就是sys_time執行的結果(或是rsp+38h-38h = rsp也可以知道) ![](https://i.imgur.com/MPNXwsW.png) - 總之還是要想辦法改變系統時間QQ ::: - 從sys_time執行後的那個function進去看,可以發現是從/etc/localtime取得運行時間的 ![](https://i.imgur.com/5mx0d4H.png) - sudo date可以短暫修改/etc/localtime,但很快就會被改回來(因為系統會根據時區抓取時間) ![](https://i.imgur.com/WtKtLHK.png) 所以可以寫個bash script不斷執行sudo date修改/etc/localtime,再執行gdb script,這樣就可以順利得到美美的flag ```python= #2018_rev.sh > ./2018_rev.sh即可執行 #!/usr/bin/env bash while true do sudo date -us "2018-01-01 00:00:00" done ``` ## 10. what-the-hell - main :::info 簡單來說就是會有兩個輸入,丟進calc_key3做運算,算出來的結果丟給decrypt_flag來算出flag ::: ![](https://i.imgur.com/69Aq6eP.png) - 而calc_key3需要先滿足某些條件才會繼續執行 :::info 1. input1 * input2 == 0xddc34132 2. (input2 + 0x10) * (input1 ^ 0x7e) == 0x732092be 3. (input1 - input2) & 0xfff == 0xcdf 4. input1是質數 ![](https://i.imgur.com/USBs9ML.png) 5. 最後有個小迴圈 ![](https://i.imgur.com/UDYQHSv.png) - what()就是在執行fib計算 -->這就是題目說something slow的原因 ![](https://i.imgur.com/mxcJGDF.png) - 上吧z3(? ```python= from z3 import * from gmpy2 import * #用sympy不知道為啥會報錯QQ a2 = BitVec('a2', 32) a1 = BitVec('a1', 32) s = Solver() s.add(a1 * a2 == 0xddc34132) s.add((a2 + 0x10) * (a1 ^ 0x7e) == 0x732092be) s.add((a1 - a2) & 0xfff == 0xcdf) while (s.check() == sat): if is_prime((s.model()[a1].as_long())): print(s.model()) s.add(Or(a1!=s.model()[a1], a2!=s.model()[a2])) #避免重複輸出 ``` - 但是還沒完,兩個答案都算超久還算不出flag,因為問題出在上面的what,這種算費式數列的方法要花很久的時間 ![](https://i.imgur.com/1bP1ClQ.png) - 但其實出題者還偷偷寫了一個fast_what的function,可以比較快的算出費式數列 ![](https://i.imgur.com/h01EyAs.png) - 所以只要把call what patch成call fast_what就可以惹 - 選中要patch的地方,Edit > patch program > assemble就可以改指令,接著再Edit > patch program > Apply patches to input file就可以成功修改了 但另一組數字一樣算不出來,猜測應該是因為不是費氏數,所以calc_key3的loop就一直執行下去惹 ![](https://i.imgur.com/F2Em0NO.png)