【渗透日记】半沉浸式详解一次从备份文件泄露到Getshell

发布于 2024-03-25  2937 次阅读


使用的软件:

  1. Visual Studio
  2. ILSpy
  3. UnauthorizedAudit
  4. sqlmap

UnauthorizedAudit是自用软件,目前以在github公开

github:UnauthorizedAudit —— 未授权代码审计工具

使用的编程语言

  • ASP.NET

前言

偶尔能在渗透过程中发现备份文件泄露的情况,备份文件在网页目录下,可以通过下载备份文件开个盲盒,有可能出sql或者源代码等等。

这个案例将用实战,以我的角度沉浸式展示从备份文件泄露,通过代码审计寻找漏洞,到利用SQL注入获取账密登录后台,再到后台权限到web服务器权限的过程。

文中所涉及的技术、思路和工具仅供以安全为目的的学习交流使用,任何人不得将其用于非法用途以及盈利等目的,否则后果自行承担!


1.文件泄露和尝试密文解密

两款小工具目录扫描工具dirsearch和备份文件扫描工具ihoneyBakFileScan_Modify

dirsearch github:https://github.com/maurosoria/dirsearch

ihoneyBakFileScan_Modify github:https://github.com/VMsec/ihoneyBakFileScan_Modify

在一次常规的EDU测试中发现了一个备份文件泄露的情况(已脱敏)

http://xxx.xxx.net/web.rar

同时找到后台管理的登录页面(尝试爆破后无果)

大概看了一下,应该是一个22年的备份,比较老了,有一个SQL文件,当然首先看有没有敏感信息

搜索password可以看到两个用户:admin、***z 这个大概率是刚刚扫描到的这个后台网页的登录用户和密码,接下来可以尝试通过这条线索尝试登录后台。

到这里 低危 备份文件泄露 + 1 Rank

当然,可以很明显的看到这段password是一段加盐加密数据,加密方式不明

因为我们拿到源代码,可以分析一下这段加密规则或者解密函数,从而逆向分析或直接调用解密函数解密出明文

可以猜测一下解密函数名可能是类似"Decrypt”之类的名称,故这里全局搜索"Decrypt"

搜索到一处目标在tools/admin_ajax.ashx.cs文件中调用过DESEncrypt.Decrypt,这个函数并不是.Net内置的解密函数。

ASP.Net程序通常会将一部分核心代码封装进dll文件中,通常是在网页目录的bin文件夹下。

将bin文件夹下的dll导入ILSpy反编译并导出。

返回源码中确认一下使用了哪些命名空间,即可定位目标"Decrypt"解密函数位置

在反编的dll中寻找着两个命名空间即可

DTcms.Common中找到DESEncrypt类,上述的"Decrypt"函数就在这里

这里我们可以忽略它是怎么做的,因为这里直接找到的解密函数,稍后可以直接调用它即可完成对密文的解密。简单分析一下,它接收两个传参,分别是TextsKeyText对应的应该是密文内容,而sKey对应的应该就是秘钥了。密文内容已知,但秘钥暂时未知,回头接着查看刚刚tools/admin_ajax.ashx.cs文件中调用DESEncrypt.Decrypt的位置,此处将从sysConfig中读取emailpassword位置的内容作为密文传入Decrypt函数,将sysConfig中读取sysencryptstring位置的内容作为秘钥传入Decrypt函数。

那很明显,首先追这个sysConfig,然后看它的sysencryptstring的值应该就是秘钥

这个sysConfig也被封装到了dll中,之前提前用ILSpy导出了代码,可以直接用Visual Studio打开,全局搜索"sysencryptstring"看能不能有结果。

搜索到在DTcms.Model下的sysconfig.cs中存在一个sysencryptstring的默认值"DTcms"

当然,它也可能存在从数据库中读取预设sysencryptstring的可能性,可以使用"DTcms"这个秘钥尝试解密之前的密文。

使用VS新建一个项目,复制粘贴刚刚的Decrypt函数到自己的项目中,然后尝试调用它

public static string Decrypt(string Text, string sKey)
{
	DESCryptoServiceProvider des = new DESCryptoServiceProvider();
	int len = Text.Length / 2;
	byte[] inputByteArray = new byte[len];
	for (int x = 0; x < len; x++)
	{
		int i = Convert.ToInt32(Text.Substring(x * 2, 2), 16);
		inputByteArray[x] = (byte)i;
	}
	des.Key = Encoding.ASCII.GetBytes(FormsAuthentication.HashPasswordForStoringInConfigFile(sKey, "md5").Substring(0, 8));
	des.IV = Encoding.ASCII.GetBytes(FormsAuthentication.HashPasswordForStoringInConfigFile(sKey, "md5").Substring(0, 8));
	MemoryStream ms = new MemoryStream();
	CryptoStream cs = new CryptoStream(ms, des.CreateDecryptor(), CryptoStreamMode.Write);
	cs.Write(inputByteArray, 0, inputByteArray.Length);
	cs.FlushFinalBlock();
	return Encoding.Default.GetString(ms.ToArray());
}

结果报错异常“不正确的数据”,密文应该是不会有问题的,那会不会是秘钥的问题呢?

一时间也找不到头绪,在这里卡了很长时间。

转念一想,在泄露的sql文件中,除了user_name和password之外还有一个salt字段,这也是当时判断它为加盐加密的原因,有没有可能就是这个密文是加盐之后的坏数据,解密函数只能解密加盐之前的数据呢?问了一下Copilot“知道密文和盐有没有可能还原密文加盐前的数据?”

行,说了但是又没完全说。

换一种思路,有没有可能这个salt的值就是秘钥

果断尝试,将秘钥的传参替换为salt位置的值。结果令人振奋,但同时又感觉是不是哪出了问题,虽然应该算是成功解密了,但是解密出的明文密码居然是“123456”?那为什么在爆破的时候没有被爆破出来。

尝试登录不出所料的密码错误。

不急,还有一个用户呢,果断解密,说实话看到这个密码的一瞬间觉得“有了”,但是结果还是密码错误

ok,面对这种情况,只有一种可能了。那就是这个22年的sql备份的用户数据,太老了,期间可能被修改。

最难过的一集。

2.通过代码审计从SQL注入到Getshell

接下来就只能从代码入手了,尝试代码审计,看看能不能有所收获

其实在刚刚尝试解密时翻看代码的过程中,就看到了一个SQL注入的点,但是演示中还是走个过程

首先这个程序是ASP.Net程序,尝试搜索ASP.Net常用功能点的关键函数或者关键字,如selectsqlStrdatabaseProcessSaveAs等等,具体函数可能会因为程序框架和书写习惯等有诸多变化,这里只做参考。因为刚刚看到一个可能的注入点嘛,我这里直接搜“sqlStr”看看存不存在sql语句

经过一番搜索,这里找到一个注入点,这是一个cs文件,ASP.Net网页不能直接访问cs文件,需要查看这个cs文件被哪个aspx页面文件调用。所搜这个文件名“cx_stu.aspx.cs”,可以看到"cx_stu.aspx"页面文件调用了这个cs文件。

访问"cx_stu.aspx"页面并构造传参,尝试触发这个漏洞

尝试打个' 试试,这个结果就非常明显了。

接下来交给sqlmap

sqlmap github:https://github.com/sqlmapproject/sqlmap

成功注入出数据库名

看了一下是DBA权限,尝试通过--os-shell建立交互式shell,这样就完成了从SQL → Getshell

但是显然是我想多了,用不同姿势尝试了很多次,最终无果。

接下来第一个想到的就是通过数据库查后台用户名和密码,直接从数据库中查询总该是正确的了吧。

这里中途查错了库,耗费了蛮多时间,最后通过CSDN的一篇文章,确定了数据库结构,才找到了存储管理员用户的地方

接下来就是密码解密,同本文第一部分一样的解密过程,这里就不再赘述。

通过解密的密码成功进入后台,真的不容易吧

Getshell寻找功能点,首先肯定是上传功能,头像上传、附件上传、图片上传、视频上传等等。由于篇幅过程这里就略过了,总不能上传功能点找不到吧。

这里主要说一下它这个系统的文件上传过滤模式,它分未两个部分,一个部分是网站后台的代码中对后缀的过滤,这里是白名单验证,第二个部分是dll中封装了一个检查文件后缀的效验函数,它是黑名单验证。当文件上传时,两个过滤会依次对后缀进行过滤。必须同时满足这两个效验通过,才可以最终上传WebShell后门文件

好在后台能够修改第一部分白名单验证中允许上传的文件类型。

这样只需要解决第二部分的黑名单验证就行了。代码如下

这里尝试一下之前绕过黑名单验证的方式,利用Windows重命名特性(Windows不允许在确认后缀名后,出现空白后缀名,例如:123.aspx....,这样命名之后aspx后面带的....会被清除,回车完成重命名后,文件名依然为123.aspx)由于CheckFileExt函数的过滤规则是黑名单过滤,即上传123.aspx....并不会命中过滤规则,当文件落地后会被windows更名为123.aspx,从而完成后门上传。

可惜这个方法在此处不可行,我们可以看一下它存储文件的方式

GetRamCode会取一个时间戳,然后加上fileExt。fileExt也就是后缀文件,它是通过判断fileName(上传文件名)尾部的"."来获取的,如果是这样的话,上传123.aspx....,此时fileName = 123.aspx....那么fileExt就会是一个空值。所以这个方式显然不可行。

后来我又尝试了使用windows命名中的非法字符,比如“?,<,>,|”之类的,尝试在文件落地后windows是否会自动更名,但是结果是在ASP.Net中根本无法上传以这些后缀结尾的文件导致报错

最后想到通过空格来绕过检测,和...同理,在后缀后面加空格,文件落地后windows也会自动去除空格。

添加一个新的文件上传类型为"aspx "(注意空格 aspx[空格])

然后修改filename="123.aspx "(注意空格),上传成功getshell

文中所涉及的技术、思路和工具仅供以安全为目的的学习交流使用,任何人不得将其用于非法用途以及盈利等目的,否则后果自行承担!

文中webshell已清理,漏洞已修复