rsp-∞

[Bomb Lab] Phase_1 본문

Write-ups/system

[Bomb Lab] Phase_1

portrait.kim 2024. 3. 23. 12:04

 

pwndbg> disassemble phase_1
Dump of assembler code for function phase_1:
   0x0000000000400ee0 <+0>: sub    rsp,0x8
   0x0000000000400ee4 <+4>: mov    esi,0x402400
   0x0000000000400ee9 <+9>: call   0x401338 <strings_not_equal>
   0x0000000000400eee <+14>: test   eax,eax
   0x0000000000400ef0 <+16>: je     0x400ef7 <phase_1+23>
   0x0000000000400ef2 <+18>: call   0x40143a <explode_bomb>
   0x0000000000400ef7 <+23>: add    rsp,0x8
   0x0000000000400efb <+27>: ret    


 

phase_1을 디스어셈블한다. 코드를 들여다보지 않고 그냥 run하면 입력값으로 무얼 넣어야 할지 전혀 모르는 채로 폭탄을 터뜨릴 수도 있다. 코드를 보면 phase_1+9와 phase_1+18에서 각각 함수를 call 하는데, 후자는 폭탄을 터뜨리는 함수 같다. phase_1+14에서 검수한 두 개의 eax가 서로 같아야 je 명령어에서 폭탄을 터뜨리는 함수를 작동시키지 않고 안전하게 phase_1+23으로 넘어간다. 문자열을 비교하는 것처럼 보이는 strings_not_equal 함수를 디스어셈블해 보자. eax의 값이 해당 함수에서 설정되는 듯하다.

 

pwndbg> disassemble strings_not_equal
Dump of assembler code for function strings_not_equal:
   0x0000000000401338 <+0>: push   r12
   0x000000000040133a <+2>: push   rbp
   0x000000000040133b <+3>: push   rbx
   0x000000000040133c <+4>: mov    rbx,rdi
   0x000000000040133f <+7>: mov    rbp,rsi
   0x0000000000401342 <+10>: call   0x40131b <string_length>
   0x0000000000401347 <+15>: mov    r12d,eax
   0x000000000040134a <+18>: mov    rdi,rbp
   0x000000000040134d <+21>: call   0x40131b <string_length>
   0x0000000000401352 <+26>: mov    edx,0x1
   0x0000000000401357 <+31>: cmp    r12d,eax
   0x000000000040135a <+34>: jne    0x40139b <strings_not_equal+99>
   0x000000000040135c <+36>: movzx  eax,BYTE PTR [rbx]
   0x000000000040135f <+39>: test   al,al
   0x0000000000401361 <+41>: je     0x401388 <strings_not_equal+80>
   0x0000000000401363 <+43>: cmp    al,BYTE PTR [rbp+0x0]
   0x0000000000401366 <+46>: je     0x401372 <strings_not_equal+58>
   0x0000000000401368 <+48>: jmp    0x40138f <strings_not_equal+87>
   0x000000000040136a <+50>: cmp    al,BYTE PTR [rbp+0x0]
   0x000000000040136d <+53>: nop    DWORD PTR [rax]
   0x0000000000401370 <+56>: jne    0x401396 <strings_not_equal+94>
   0x0000000000401372 <+58>: add    rbx,0x1
   0x0000000000401376 <+62>: add    rbp,0x1
   0x000000000040137a <+66>: movzx  eax,BYTE PTR [rbx]
   0x000000000040137d <+69>: test   al,al
   0x000000000040137f <+71>: jne    0x40136a <strings_not_equal+50>
   0x0000000000401381 <+73>: mov    edx,0x0
   0x0000000000401386 <+78>: jmp    0x40139b <strings_not_equal+99>
   0x0000000000401388 <+80>: mov    edx,0x0
   0x000000000040138d <+85>: jmp    0x40139b <strings_not_equal+99>
   0x000000000040138f <+87>: mov    edx,0x1
   0x0000000000401394 <+92>: jmp    0x40139b <strings_not_equal+99>
   0x0000000000401396 <+94>: mov    edx,0x1
   0x000000000040139b <+99>: mov    eax,edx
   0x000000000040139d <+101>: pop    rbx
   0x000000000040139e <+102>: pop    rbp
   0x000000000040139f <+103>: pop    r12
   0x00000000004013a1 <+105>: ret    

 


rdi는 첫 번째 인자값, rsi는 두 번째 인자값이다. 각각 rbx, rbp에 값을 대입하고 있다. 실질적인 데이터 없이는 코드의 흐름을 파악하기가 복잡해 임의의 값을 넣어 보고자 했다. 그런데 값을 받은 어떤 함수가 어떻게 동작하고 값을 처리했는지 보려면 phase_1을 그냥 run하는 것이 아니라, 함수에서 나의 입력값을 처리하기 전과 후의 변화를 살펴보아야 한다.

 

함수가 시작하기 전과 작동을 마친 후에 break point를 걸어 준다.

pwndbg> b *strings_not_equal
Breakpoint 1 at 0x401338
pwndbg> b *strings_not_equal+105
Breakpoint 2 at 0x4013a1

 

이 상태로 run을 하여 임의의 값을 넣어 보았다.

 

Breakpoint 1, 0x0000000000401338 in strings_not_equal ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
────────────────────────────────────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]─────────────────────────────────────────────────────────────────────
*RAX  0x603780 (input_strings) ◂— 0x796177627573 /* 'subway' */
 RBX  0x0
*RCX  0x6
*RDX  0x1
*RDI  0x603780 (input_strings) ◂— 0x796177627573 /* 'subway' */
*RSI  0x402400 ◂— outsd dx, dword ptr [rsi] /* 'Border relations with Canada have never been better.' */
 R8   0x0
*R9   0x7ffff7d7d210 (__memcpy_ssse3+9728) ◂— mov edx, dword ptr [rsi - 6]
*R10  0x7ffff7c0c888 ◂— 0xf001200001010
*R11  0x7ffff7c3a360 (__ctype_b_loc) ◂— endbr64 
*R12  0x7fffffffdf18 —▸ 0x7fffffffe288 ◂— '/home/chaehyeonkim/Documents/bomb/bomb'
*R13  0x400da0 (main) ◂— push rbx
 R14  0x0
*R15  0x7ffff7ffd040 (_rtld_global) —▸ 0x7ffff7ffe2e0 ◂— 0x0
*RBP  0x1
*RSP  0x7fffffffdde8 —▸ 0x400eee (phase_1+14) ◂— test eax, eax
*RIP  0x401338 (strings_not_equal) ◂— push r12


 

RAX에 입력받은 문자열로 내가 넣은 subway라는 값이 들어 있다. RSI에는 'Border relations with Canada have never been better.' 라는 값이 들어 있다. continue하여 다음 break point인 strings_not_equal+105까지 가 보자.

 

 

Breakpoint 2, 0x00000000004013a1 in strings_not_equal ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
────────────────────────────────────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]─────────────────────────────────────────────────────────────────────
*RAX  0x1
 RBX  0x0
 RCX  0x6
 RDX  0x1
*RDI  0x402400 ◂— outsd dx, dword ptr [rsi] /* 'Border relations with Canada have never been better.' */
 RSI  0x402400 ◂— outsd dx, dword ptr [rsi] /* 'Border relations with Canada have never been better.' */
 R8   0x0
 R9   0x7ffff7d7d210 (__memcpy_ssse3+9728) ◂— mov edx, dword ptr [rsi - 6]
 R10  0x7ffff7c0c888 ◂— 0xf001200001010
 R11  0x7ffff7c3a360 (__ctype_b_loc) ◂— endbr64 
 R12  0x7fffffffdf18 —▸ 0x7fffffffe288 ◂— '/home/chaehyeonkim/Documents/bomb/bomb'
 R13  0x400da0 (main) ◂— push rbx
 R14  0x0
 R15  0x7ffff7ffd040 (_rtld_global) —▸ 0x7ffff7ffe2e0 ◂— 0x0
 RBP  0x1
 RSP  0x7fffffffdde8 —▸ 0x400eee (phase_1+14) ◂— test eax, eax
*RIP  0x4013a1 (strings_not_equal+105) ◂— ret 


 

내가 입력한 값이 들어가 있던 RAX가 함수를 끝마친 뒤에는 0x1이라는 값을 가지고 있다. strings_not_equal 함수에서 사용되는 movzx 함수는 mov의 확장형으로, 첫 번째 데이터가 두 번째 데이터보다 크기가 커도 복사가 되도록 한다고 한다.

 

pwndbg> disassemble string_length
Dump of assembler code for function string_length:
   0x000000000040131b <+0>: cmp    BYTE PTR [rdi],0x0
   0x000000000040131e <+3>: je     0x401332 <string_length+23>
   0x0000000000401320 <+5>: mov    rdx,rdi
   0x0000000000401323 <+8>: add    rdx,0x1
   0x0000000000401327 <+12>: mov    eax,edx
   0x0000000000401329 <+14>: sub    eax,edi
   0x000000000040132b <+16>: cmp    BYTE PTR [rdx],0x0
   0x000000000040132e <+19>: jne    0x401323 <string_length+8>
   0x0000000000401330 <+21>: repz ret 
   0x0000000000401332 <+23>: mov    eax,0x0
   0x0000000000401337 <+28>: ret


 

스터디에서 다룬 부분은 아니지만 문자열을 비교하는 함수 안에서 문자열의 길이를 비교할 필요성이 보여 디스어셈블해 보았다. 0x0과 rdi(우리가 인자로 넘긴 값이다)를 비교하고, ZF가 나오면 string_length+23으로 이동하여 eax에 0x0을 삽입한다.

그러면 내가 입력한 값을 받은 함수의 작동이 끝났을 때 RAX에서 가지고 있는 값도 0x0이어야 할 것 같다. rdi로 넣은 값이 RAX에 일차적으로 저장되므로 이하 코드에서 확인할 수 있는 rsi와 같은 문자열을 입력해 보자.

 

pwndbg> run
Starting program: /home/chaehyeonkim/Documents/bomb/bomb 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Welcome to my fiendish little bomb. You have 6 phases with
which to blow yourself up. Have a nice day!
Border relations with Canada have never been better.
Phase 1 defused. How about the next one?

 

Phase 1을 해결한 것을 알 수 있다.