-
-
[原创]CTF php .so pwn 题型分析
-
发表于: 2024-6-4 13:18 20104
-
2024-05-28-PHP-So-Pwn
基础的一些知识
-
最近打比赛 遇到了几道 php so 模块的 pwn 题,个人感觉挺有意思的
-
一般情况下会把php 大部分函数 ban 掉,只是用 自定义so 文件里的函数
.so 文件的导出函数
虽然里面函数名前面有zif_
,实际上调用的函数名是
1
2
3
4
5
|
add_chunk() show_chunk() edit_chunk() edit_name() free_chunk() |
gdb调试方法(一种)
- 可以先把本地的 gdbserver 上次到docker, 能正常运行就行了
- 我只开了一个 docker 环境,正常情况下,他的ip 就是 这个加1 ,也就是
172.17.0.2
- 然后用 gdbserver 启动
- 上面是调试的方法,
函数传参分析
- 分析一下正常情况的状态
1
2
3
4
5
6
7
8
|
|
- gdb 调试
1
2
|
rdi 第一个参数可能是 传递参数的数量, rsi 可以通过该指针指向的字符数量来得知正确的参数数量(当前add_chunk) `lzz` 3个参数 类型 |
- 然后后面参数里的地址应该是用于存放我们传入参数的指针
zend_parse_parameters
最简单的获取函数调用者传递过来的参数便是使用zend_parse_parameters()函数。zend_parse_parameters()函数的前几个参数我们直接用内核里宏来生成便可以了,形式为:ZEND_NUM_ARGS() TSRMLS_CC,注意两者之间有个空格,但是没有逗号。从名字可以看出,ZEND_NUM_ARGS()代表着参数的个数。紧接着需要传递给zend_parse_parameters()函数的参数是一个用于格式化的字符串,就像printf的第一个参数一样。下面表示了最常用的几个符号。
- 数字类型判断
1
2
3
|
4 int 6 strings 7 arrary |
- 类型 传擦和返回值,用于恢复结构
1 |
int zend_parse_parameters( size_t arg_num, char *TypeChar, ...)
|
-
如果参数不是预期的数量和类型,
zend_parse_parameters
会返回 -1 否则 0 -
简单了解下
lzz
是什么东东
1
2
3
4
5
6
7
8
9
10
11
|
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 之前没有的
|
- 然后是简单分析的图
- 这部分来自星盟安全的pwnshell wp
bin_data_size的映射表,将宏定义展开为如下数组所示:
1 |
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 ...}; |
- 接下来来分析几道题目
NumberGame
- 来自 第一届“长城杯”信息安全铁人三项赛决 夺取闯关 pwn numbergame
题目环境
-
server.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
#!/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)
|
-
Dockerfile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
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"
|
- 然后
php.ini
基本上禁用了所有的php函数
[PHP] disable_functions = "system",... disable_classes = ""... extension = numberGame.so ; 扩展的 so
- 给了
.tar.gz
的docker 镜像包,使用以下命令进行加载(线下比赛没有网络 给Dockerfile
也拉起不了,所以给打包的环境)
1 |
docker load -i jingxiang. tar .gz
|
- 启动环境
1 |
docker run - - name run_numbergame - p 5555 : 5555 - itd numbergame:latest
|
分析 numberGame.so 漏洞
触发漏洞
大致结构体
1
2
3
4
5
6
|
struct mechunk{
size_t *name_ptr;
size_t num;
size_t array_size; // 默认 最大 100, 通过漏洞使这里变大,我们既可以实现数组越界
...;
}; |
正常情况下,array_size 是 4
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
赞赏记录
参与人
雪币
留言
时间
apwnaroot
+1
你的帖子非常有用,感谢分享!
2024-8-11 20:18
PLEBFE
看雪因你而更加精彩!
2024-6-21 02:11
Tokameine
谢谢你的细致分析,受益匪浅!
2024-6-6 21:24
赞赏
他的文章
赞赏
雪币:
留言: