某订货系统文件上传漏洞分析

0x01 鉴权分析 ========= 在这个系统中,对用户有两层鉴权: 第一层是使用了.NET中的一个 HTTP 模块`FormsAuthenticationModule`方法,它的主要作用是管理用户的身份验证过程,包括用户登录、身份验证票据管理、重定向到登录页面等。在处理主要逻辑的`OnEnter`方法中 ![image-20240718113945094](https://shs3.b.qianxin.com/butian_public/fc8a5d4fff1e6d013d0a47a98c8de911a.png) 首先会通过`AuthenticationConfig.AccessingLoginPage`检查当前请求是否在访问登录页面,如果正在访问登录页面,则直接跳过授权步骤。如果没有跳过授权检查,则调用 `SetSkipAuthorizationNoDemand` 方法,根据请求是否为有效的 Web 资源请求来决定是否跳过授权检查。 第二层鉴权方式在`AdminPage`类中,是该系统自己实现的方法,执行类的时候需要继承该类,首先查看`OnInit`方法,它在页面初始化阶段被调用,用于执行初始化工作。 ![image-20240717183441073](https://shs3.b.qianxin.com/butian_public/fc3262b76a5a8e1f02ad5ce952b01a736.png) 在`CheckPageAccess`中会检查当前用户是否有访问当前页面的权限。如果不符合条件则被重定向到其他页面,如果都符合则继续后面的流程 1. 检查用户是否是站点管理员。如果不是,将其重定向到登录页面。 2. 如果是站点管理员,检查用户是否是管理员。如果不是,检查当前页面是否需要管理员权限。如果需要,将其重定向到访问被拒绝页面。 3. 检查用户是否具有页面所需的特定权限。如果没有,将其重定向到访问被拒绝页面,并附带权限信息。 ![image-20240717183615702](https://shs3.b.qianxin.com/butian_public/fa0cc90624a8e3aca52041eee174fe36a.png) 在寻找哪里有授权操作的时候发现登录逻辑处存在一个后门账户SuperMember,密码为MdYdt2017Admin,会直接登录userid为1101的账户,通过数据库查看1101正好为admin管理员账户 ![image-20240718160117767](https://shs3.b.qianxin.com/butian_public/fc3d811e992f93344500029de7668dec4.png) ![image-20240718160056589](https://shs3.b.qianxin.com/butian_public/f423b12b2e617b67cd8e263b6da5c2db4.png) 从上面两个鉴权方式我们还发现`Iuser user`的获取方式也有两种,一种是通过`HiContext.Current.User`方式获取当前user对象,另一种是通过`Users.GetUser`的方式获取user对象,事实上我们查看`HiContext.Current.User`方法,他也是通过`Users.GetUser`方法获取的。那么我们就主要来看看`Users.GetUser`方法 ![image-20240718184831291](https://shs3.b.qianxin.com/butian_public/f2dd7f3e8ff8dd1730448c4e61b7c68bf.png) 当鼠标放在`Users.GetUser`方法上时就会显示该方法有3种重载, ![image-20240718184958172](https://shs3.b.qianxin.com/butian_public/fa569ca39d5a168efd372675deff250d8.png) 分别是`IUser GetUser()`, 会通过`GetLoggedOnUsername()`方法获取用户名最后返回user对象 ![image-20240719101305002](https://shs3.b.qianxin.com/butian_public/f5cb7e2e960d3d5c7871df7b2b24ccd59.png) `IUser GetUser(int userId)`和`GetUser(int userId, bool isCacheable)`,会通过userId获取 ![image-20240719101333158](https://shs3.b.qianxin.com/butian_public/f1b61605c36cf7435c1b1e1ff39edc04c.png) `IUser GetUser(int userId, string username, bool isCacheable, bool userIsOnline)`通过userId或username获取最后返回user对象 ![image-20240719101404051](https://shs3.b.qianxin.com/butian_public/fde04f3e78026c3c1cfd13f5d8ada5c7c.png) 所以说我们只要可以控制userId或username可控,且流程的后面不验证密码,那么就可以通过鉴权。 比较有意思的是`GetUser()`中获取username的方法是`GetLoggedOnUsername()`,而在这个方法中,是直接取了cookie中的`Vshop-Member`参数作为username的,所以如果其他地方直接调用了`GetUser()`或者先调用`GetLoggedOnUsername()`再将获取的值传递给下面重载函数中也可以通过鉴权。 ![image-20240719102111540](https://shs3.b.qianxin.com/butian_public/f948c67eac226d7e02d0e08546729d590.png) 在这个漏洞中,由于该类继承自`AdminPage`,所以无法通过`GetLoggedOnUsername`方法绕过,但是由于系统存在后面账户,所以可以通过后门账户登陆后进行上传操作。 0x02 漏洞分析-文件上传 ============== 该系统存在很多文件上传的接口,但是大部分都对上传的文件进行了检查和限制 ![image-20240718160514849](https://shs3.b.qianxin.com/butian_public/f6ca2a05ea21de8930a64faee0ab5f8ac.png) 发现白名单上传时可以上传zip文件,所以如果可以找到解压zip相关的方法,也许可以上传webshell。 搜索解压的关键字,如zipFIle,ZipEntry等。找到`PrepareDataFiles`方法中调用了`ZipFile.Read`并且在下面遍历过程中解压了文件 ![image-20240718174749161](https://shs3.b.qianxin.com/butian_public/f6016802039fe0c5e55196e12c73a2b11.png) 而`PrepareDataFiles`方法正在在上面的`btnUpload2_Click`方法中调用,并且通过ShowMsg看出应该是个上传模板的地方 ![image-20240718174845703](https://shs3.b.qianxin.com/butian_public/f44d12284fa8fdb78c86ec9ad7c8d73c3.png) 对应的类为`ManageThemes`首选寻找`ManageThemes`类对应的aspx文件 ​ ![image-20240717174025229](https://shs3.b.qianxin.com/butian_public/f17a3d40e0e57793b45be03ce57981ac6.png) 但是直接访问该aspx文件会跳转到登陆页面,看到路径里有admin,所以应该是一个后台的功能。 ![image-20240717163311155](https://shs3.b.qianxin.com/butian_public/ff59b06677059c7ebbc5557fcd4bb74af.png) 首先来看`Page_Load`方法,她通常用于处理页面加载时的初始化逻辑。在该方法中首先会解析xml文件,然后分别为 `rptManageThemes` 控件的 `ItemCommand` 事件和 `btnUpload2` 控件的 `Click` 事件添加事件处理程序,分别指定为 `this.rptManageThemes_ItemCommand` 和 `this.btnUpload2_Click` 方法。 这里我们需要触发的是`btnUpload2_Click`方法,但是当我们打开对应页面时,却发现页面中并没有上传相关的地方 ![image-20240717163807027](https://shs3.b.qianxin.com/butian_public/f9515b5508f4075e705c00cc4fc8c0ae5.png) 不死心的我在源代码中搜索上传的关键字`btnUpload2`发现确实存在相关功能,发现只是通过css隐藏起来了 ![image-20240717164317915](https://shs3.b.qianxin.com/butian_public/f24b19cb1a9a7fa511019da34355febb3.png) 直接将style中的`display: none`删除即可看到上传模板的功能 ![image-20240717164343107](https://shs3.b.qianxin.com/butian_public/f73b7f5bb66923ea5f0a785fafb1ea4e8.png) 这里我们可以上传一个带有木马文件的压缩包,除此之外,压缩包里必须还需要有一个以模板文件命名的xml文件,否则会在`Page_Load`方法中因读不到文件而造成报错。 ![image-20240717184054530](https://shs3.b.qianxin.com/butian_public/faba96dfb21ac6f05807eb55838096fe6.png) 在页面选择文件上传后依然会出现问题,提示无法获取对应模板名称 ![image-20240717184142639](https://shs3.b.qianxin.com/butian_public/f6c2b036d208c4b405e3d370d6b6f81df.png) 继续查看代码是怎么回事,发现这里判断是否存在`hdtempname`的值,并且判断数据包ContentType以及判断`hdtempname`的值与压缩包文件名是否一样 ![image-20240717184234282](https://shs3.b.qianxin.com/butian_public/f0837c8742d67ced48c9032b52267e49c.png) 通过抓包查看,发现默认上传的时候`hdtempname`的值为空,将其修改为test发包即可上传成功 ![image-20240718102832587](https://shs3.b.qianxin.com/butian_public/f4701117cfdaf6c04e6c5aacf91da1919.png) 然后访问/Templates/master/pc/test/1.aspx ![image-20240718102943642](https://shs3.b.qianxin.com/butian_public/fdddcc248a963326d2ada2b2f2e6fd3d6.png) 在这个漏洞中,由于对应的类`ManageThemes`在admin路径下,且`ManageThemes`这个类继承自`AdminPage`,所以需要管理员的才可以执行。由于在`adminPage`中并没有直接调用`GetLoggedOnUsername`方法,所以我们只能使用后门账户进行登陆后操作。 0x03 漏洞分析-远程文件下载 ================ 上传文件除了可以通过本地上传,有时候也可以通过下载远程文件保存到服务器上。 在C#中远程下载文件保存到本地有两种方法:使用`WebClient`类或`HttpClient`类。 分别搜索这两个关键字,最后锁定这样一处,`webClient.DownloadFile`的第一个参数是下载的url,第二个参数是保存的路径 ![image-20240719114401997](https://shs3.b.qianxin.com/butian_public/ff5b5ccbf87d9071d826f731d67bb60ec.png) 这里的下载url也是可控的,通过表单的`ImageUrls`传参 ![image-20240719114519415](https://shs3.b.qianxin.com/butian_public/f72ef541ffddf0f85cc9bc8979c379825.png) 再向上追溯该方法`ProcessTaobaoProductDown`的调用,发现在`ProcessRequest`里,该方法是 `IHttpHandler` 接口的一部分,定义了一个方法来处理传入的 HTTP 请求。 ![image-20240719134027378](https://shs3.b.qianxin.com/butian_public/f40e56163e975120a55e428fb9ebe926a.png) 同时该方法属于`TaobaoProductHandler`,我们在文件中寻找是否有该字样,发现对应的ashx文件。由于该ashx文件时位于`API`目录下,并且没有继承于`Adminpage`所以该方法不需要鉴权。 ![image-20240719134349761](https://shs3.b.qianxin.com/butian_public/fc372758f426cbffb7d0141718fd58770.png) 然后我们使用vps启动一个服务并上传一个木马文件,然后构造payload访问 ![image-20240719135620375](https://shs3.b.qianxin.com/butian_public/f0517262ad1d6147133c9c520fd4230c5.png) 虽然报错了,但实际上是成功下载了的,报错是因为下载之后的步骤,我们也无需理会了。这样木马文件就下载到了服务器,但这里还有一个问题,那就是保存文件的文件名是通过`Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture)`方法随机生成32位字符的文件名取代,路径为`/Storage/master/product/images/`,所以我们还需要想办法获取下载的文件名。 问了一下chatgpt,可以读文件目录的方法也有很多。 ![image-20240719142036964](https://shs3.b.qianxin.com/butian_public/f0aa8d9186c00c1399ce66f4ca8d9076b.png) 在`FillTableForPath`方法中调用了`Directory.GetFiles`,在函数的下方对获取到的files进行遍历,并将相关信息放入hashtable中,然后将hashtable添加到list中,而list又被赋值给`table["file_list"]`,这里的table也是一个传过来的Hashtable ![image-20240719151712015](https://shs3.b.qianxin.com/butian_public/f2a924c46ec4fbed6032cb673f8a5315d.png) ![image-20240719151626619](https://shs3.b.qianxin.com/butian_public/f78b7bfe35d3aec986e7afefd87dd613a.png) 然后向上追溯`FillTableForPath`方法,查看第一个参数text2是否可控,从上面可以看出text2是从通过路径拼接text得来的,而text是通过cid参数赋值,参数可控,可以通过路径穿越获取指定目录的所有文件信息。 ![image-20240719152108776](https://shs3.b.qianxin.com/butian_public/f14aeea19bd66426b93edf426eaff89aa.png) 但是这个方法是有鉴权的,前面的鉴权分析,`User.GetUser`的前两个参数要是有一个可控就可以通过鉴权,这里的第二个参数是`Users.GetLoggedOnUsername()`,该方法可以直接通过cookie中的`Vshop-member`参数获取用户名,所以只需要在发包的时候在cookie中添加`Vshop-Member=admin`即可。 ![image-20240719102111540](https://shs3.b.qianxin.com/butian_public/f948c67eac226d7e02d0e08546729d590.png) 发送payload 成功获取到该目录下的所有文件信息 ![image-20240719153039737](https://shs3.b.qianxin.com/butian_public/f9313fd8c6f7fc11a3675093fac1f0fe9.png) 访问目标文件也可以正常解析。 ![image-20240719153320250](https://shs3.b.qianxin.com/butian_public/ffe346c561a8581ffe5aa6317d06dc97a.png) 在这个漏洞中,首先通过远程下载木马文件,但由于保存的是随机文件名,所以我们需要获取到文件名。在获取文件名的方法和类中,由于该类没有继承`AdminPage`,但在方法中有鉴权,所以通过`GetLoggedOnUsername`方法进行绕过鉴权,从而获取到文件名成功getshell。
原始链接: https://forum.butian.net/share/3835
侵权请联系站方: [email protected]

相关推荐

换一批