$ file 00_angr_find
00_angr_find: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=33a8c07c85370a2b45a17cc78b035b56d2dd7f8e, not stripped
파일을 실행하면 비밀번호를 입력 받고 틀리면 “Try again.”을 출력해준다.
옮을 경우 출력해주는 루틴도 있을 것이다.
더 정확하게 분석하기 위해 코드분석을 해보자.
int __cdecl main(int argc, const char **argv, const char **envp)
{
signed int i; // [esp+1Ch] [ebp-1Ch]
char s1[9]; // [esp+23h] [ebp-15h]
unsigned int v6; // [esp+2Ch] [ebp-Ch]
v6 = __readgsdword(0x14u);
printf("Enter the password: ");
__isoc99_scanf("%8s", s1);
for ( i = 0; i <= 7; ++i )
s1[i] = complex_function(s1[i], i);
if ( !strcmp(s1, "ILIUFVJF") )
puts("Good Job.");
else
puts("Try again.");
return 0;
}
int __cdecl complex_function(signed int a1, int a2)
{
if ( a1 <= '@' || a1 > 'Z' )
{
puts("Try again.");
exit(1);
}
return (3 * a2 + a1 - 'A') % 26 + 'A';
}
angr를 사용하므로 복잡한 함수에서 계산을 구해서 입력값을 구하지 않고, “Good Job.” 경로로 가는 주소를 찾으면 된다.
.text:0804865C loc_804865C: ; CODE XREF: main+4F↑j
.text:0804865C cmp [ebp+var_1C], 7
.text:08048660 jle short loc_804862F
.text:08048662 sub esp, 8
.text:08048665 push offset s2 ; "ILIUFVJF"
.text:0804866A lea eax, [ebp+s1]
.text:0804866D push eax ; s1
.text:0804866E call _strcmp
.text:08048673 add esp, 10h
.text:08048676 test eax, eax
.text:08048678 jz short loc_804868C
.text:0804867A sub esp, 0Ch
.text:0804867D push offset s ; "Try again."
.text:08048682 call _puts
.text:08048687 add esp, 10h
.text:0804868A jmp short loc_804869C
.text:0804868C ; ---------------------------------------------------------------------------
.text:0804868C
.text:0804868C loc_804868C: ; CODE XREF: main+9A↑j
.text:0804868C sub esp, 0Ch
.text:0804868F push offset aGoodJob ; "Good Job."
.text:08048694 call _puts
.text:08048699 add esp, 10h
.text:0804869C
main() 함수에서 좀더 low-level로 처리 과정을 살펴보면, _strcmp 함수 호출 결과 리턴값이 eax 레지스터에 저장되는데, test eax, eax는 eax AND eax 결과가 0인지 음수인지 확인하는 것으로 0이면 jz, 즉 zero이면 loc_804868C로 jump한다.
0x804868C 주소에서 코드를 확인해보면 “Good Job.”을 출력해주는 루틴이다.
따라서 0x804868C가 angr를 이용해서 찾아야 하는 주소이다.
#!/usr/bin/python
import angr
# address to find
FIND = 0x804868C
# create angr project
project = angr.Project('./00_angr_find', auto_load_libs=False)
# tell angr where to start executing (main() or somewhere)
# add options to indicate angr to start from main()
state = project.factory.entry_state(add_options={angr.options.SYMBOL_FILL_UNCONSTRAINED_MEMORY, angr.options.SYMBOL_FILL_UNCONSTRAINED_REGISTERS})
# create a simulation manager and initialize it with starting state
#simgr = project.factory.simulation_manager(state)
simgr = project.factory.simgr(state)
# explore possible path while executing binary
simgr.explore(find=FIND)
if simgr.found:
# if address found, state saved in found stash. if failed it is empty
print(simgr.found[0])
# print the string that angr wrote to stdin to find solution
print(simgr.found[0].posix.dumps(0))
else:
raise Exception('Could not find the solution')
위와 같이 코드를 작성하고 실행해보자.
“Good Job.”을 출력해주는 루틴으로 갈 수 있는 입력값은 “IICLTGRK”다.
다시 실행 파일을 실행해서 해당 문자열을 입력으로 주면 “Good Job.”이 출력된다.
[angr_ctf] 02_angr_find_condition (0) | 2022.05.14 |
---|---|
[angr_ctf] 01_angr_avoid (0) | 2022.05.14 |
GoogleCTF 2016 unbreakable_0 (0) | 2022.05.11 |
[DefCamp CTF 2015 Quals] Entry Language (Reverse 100) (0) | 2022.05.11 |
[angr] fauxware (0) | 2022.05.09 |