64bit 바이너리이다.
signed __int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
signed __int64 result; // rax
char s; // [rsp+0h] [rbp-110h]
unsigned __int64 v5; // [rsp+108h] [rbp-8h]
v5 = __readfsqword(0x28u);
printf("Enter the password: ", a2, a3);
if ( !fgets(&s, 255, stdin) )
return 0LL;
if ( (unsigned int)sub_4006FD(&s, 255LL) )
{
puts("Incorrect password!");
result = 1LL;
}
else
{
puts("Nice!");
result = 0LL;
}
return result;
}
signed __int64 __fastcall sub_4006FD(__int64 a1)
{
signed int i; // [rsp+14h] [rbp-24h]
const char *v3; // [rsp+18h] [rbp-20h]
const char *v4; // [rsp+20h] [rbp-18h]
const char *v5; // [rsp+28h] [rbp-10h]
v3 = "Dufhbmf";
v4 = "pG`imos";
v5 = "ewUglpt";
for ( i = 0; i <= 11; ++i )
{
if ( (&v3)[i % 3][2 * (i / 3)] - *(char *)(i + a1) != 1 )
return 1LL;
}
return 0LL;
}
.text:00000000004007E8 main proc near ; DATA XREF: start+1D↑o
.text:00000000004007E8
.text:00000000004007E8 s = byte ptr -110h
.text:00000000004007E8 var_8 = qword ptr -8
.text:00000000004007E8
.text:00000000004007E8 ; __unwind {
.text:00000000004007E8 push rbp
.text:00000000004007E9 mov rbp, rsp
.text:00000000004007EC sub rsp, 110h
.text:00000000004007F3 mov rax, fs:28h
.text:00000000004007FC mov [rbp+var_8], rax
.text:0000000000400800 xor eax, eax
.text:0000000000400802 mov edi, offset format ; "Enter the password: "
.text:0000000000400807 mov eax, 0
.text:000000000040080C call _printf
.text:0000000000400811 mov rdx, cs:stdin ; stream
.text:0000000000400818 lea rax, [rbp+s]
.text:000000000040081F mov esi, 0FFh ; n
.text:0000000000400824 mov rdi, rax ; s
.text:0000000000400827 call _fgets
.text:000000000040082C test rax, rax
.text:000000000040082F jz short loc_400866
.text:0000000000400831 lea rax, [rbp+s]
.text:0000000000400838 mov rdi, rax
.text:000000000040083B call sub_4006FD
.text:0000000000400840 test eax, eax
.text:0000000000400842 jnz short loc_400855
.text:0000000000400844 mov edi, offset s ; "Nice!"
.text:0000000000400849 call _puts
.text:000000000040084E mov eax, 0
.text:0000000000400853 jmp short loc_40086B
.text:0000000000400855 ; ---------------------------------------------------------------------------
.text:0000000000400855
.text:0000000000400855 loc_400855: ; CODE XREF: main+5A↑j
.text:0000000000400855 mov edi, offset aIncorrectPassw ; "Incorrect password!"
.text:000000000040085A call _puts
.text:000000000040085F mov eax, 1
.text:0000000000400864 jmp short loc_40086B
IDA로 확인한 어셈블리 코드다.
main() 함수 if문에서 조건에 따라 분기하여 가는 두 주소를 찾는다.
sub_4006FD 함수 리턴값이 eax 레지스터에 저장되는데, `test eax, eax` 로 비교 후 `jnz short loc_400855`에 따라 결과가 0이 아닐 경우, 즉 참일 경우 0x400855 주소로 이동한다. 0이면 jnz 코드 다음 주소인 0x400844로 이동한다.
따라서 “Nice”가 출력되는 0x400844 주소로 이동하는 경로를 찾아야 하고, “Incorrect password!”를 출력하는 0x400855 주소로 이동하는 경로를 피해야 한다.
Simulation Managers에서는 explore() API를 통해 find, avoid에 주소를 지정하여 실행 시 찾는 주소에서 실행을 멈출 수 있다. 실행을 진행하면서 active stash에서의 상태가 find 조건과 일치하면, found stash로 상태를 옮기고 실행을 종료한다.
#!/usr/bin/python
import angr
# load binary
p = angr.Project('r100', auto_load_libs=False)
# get state from entrypoint
state = p.factory.entry_state()
# use simulation manager
simgr = p.factory.simgr(state)
# search path
simgr.explore(find=0x400844, avoid=0x400855)
print(simgr.found)
print(simgr.found[0].posix.dumps(0))
코드를 실행하면, found stash에 0x400844 주소에서의 상태가 저장된 것을 확인할 수 있다. found stash에 저장된 상태에서 Code_Talkers 문자열을 확인할 수 있다.
Code_Talkers가 비밀번호인 것 같아서 입력값으로 줬더니 “Nice!”가 출력된걸 확인할 수 있다.
#!/usr/bin/python
import angr
# load binary
p = angr.Project('r100', auto_load_libs=False)
# get state from entrypoint
state = p.factory.entry_state()
# use simulation manager
simgr = p.factory.simgr(state)
# search path
simgr.explore(find=lambda s:b'Nice' in s.posix.dumps(1))
print(simgr.found)
print(simgr.found[0].posix.dumps(0))
print(simgr.found[0].posix.dumps(1))
[angr_ctf] 00_angr_find (0) | 2022.05.14 |
---|---|
GoogleCTF 2016 unbreakable_0 (0) | 2022.05.11 |
[angr] fauxware (0) | 2022.05.09 |
[QWBCTF 2018] core writeup (kernel exploit) (0) | 2022.03.22 |
[0CTF 2018] baby kernel 2(kernel exploit, double fetch, race condition) (0) | 2021.11.11 |