浅谈Asp代码审计之某cms通读实例(二)

 

0x1 前言(Preface)

阅读此文之前,如果不会搭建相关审计环境,建议从我第一篇文章开始看起浅谈Mac上手 Asp and Asp.net 代码审计(一),关于asp的审计应该是很久以前的东西,但是经常在内网渗透或者红队中遇到asp的老系统,同时也为了扩展自己的知识面,在web方向打下基础。此篇文章可能比较小众,欢迎各位师傅拍砖指点。(Ps.此教程还是从小白的角度出发,因为作者本身就是小白)

 

0x2 审计对象(Audit Object)

这个系统是不是感觉有点怀旧的感觉,这就是新云网站管理系统V3.0.0,源码可以在网上搜索下载到,

 

0x3 asp代码审计一些特点(Feature)

asp程序写的真的很原始,读起来逻辑很连贯,而且基本都是单入口的,包含文件内容一般是一些全局配置的内容,所以通读asp程序,就是逐个文件去读,当然有些小技巧,提高阅读速度,快速定位有弱点的位置,下面就是分享下自己的学习过程啦。

小白是不是感觉自己不懂vbscript就不能上手asp代码审计了呢? 那么可以先补充下基础知识再继续学习,如下是vbscript的基础语法

asp程序一般支持VBSCRIPT 和 jscript ,但是常见的都是vbscript语法,所以我们只说下vbscript的基础语法。

asp关键词基本都是大驼峰命名,即首字母大写,但是vbscript语法是不区分大小写

asp文件VBscript标识开头:

<%@ LANGUAGE = VBScript CodePage = 936%>

变量声明:

Dim html

数组:

Dim array(2) ‘跟我们平时学的大小不太一样

Dim array(2,3) ‘3行4列多维数组

单行注释:

'

字符串链接符:

&

字符串:

必须用双引号括起来

常用输出:

Response.Write("Hello World!")

for循环语句:

for i=0 to 3:
    ......
next

Do
....
Loop

For Each .... Next ...

流程控制语句:

If ... Then ... '只执行一条语句
If... Then ... End If '可以执行多条语句
If ... Then ... Else ...
If ... Then ... ElseIf ...
Select ... Case

过程:

' --  Sub过程
Sub Name(str1, str2)
    ........
End Sub
' -- 调用
1.Call Name(str1, str2)
2. Name ' --无参调用
' --  Function
Function Name(number)
.........
End Function

' -- Function 过程
Function GetName()
Response.Write "test"
End Function
GetName
' --- 区别
' -- Sub没有返回值,而Fubnction有返回值。
' -- Sub不能放在表达式中,而Function可以。

类的使用:

Class ClassName
Type  VariableName
Private Sub Class_Initialize() '构造过程
Reponse.Write("class initialize starting")
End Sub
Private Sub Class_Terminate() '析构过程
Response.Write("class end")
End Class

下面是我在熟悉这些语法的时候本地练习的代码:

<%@ Language = vbscript CodePage = 936 %>
<%
Dim html 'declaration,i
Dim array(2)
array(0) = "array[0]"
array(1) = "array[1]"
array(2) = "array[2]"
html="123" 'value  variable
Response.Write("123" & html & "</br>") 'output result
for i=0 to 2
    Response.Write(i)
    Response.Write(array(i) & "</br>")
next
Dim Num1,Num2
Num1 = 10 
Num2 = 20
If Num1 < Num2 Then Response.Write("Num1 < Num2 ! Right!")

Response.Write("</br>")

If Num1 < Num2 Then
    Response.Write("Num1 < Num2 ! Right! 1")
    Response.Write("Num1 < Num2 ! Right! 2")
End If

Response.Write("</br>")

If Num2 < Num1 Then
    Response.Write("Num1 > Num2 ! Right!")
Else
    Response.Write("Num1 < Num2 ! Right!")
End If

Response.Write("</br>")

Response.Write("Select Case Example")
Response.Write("</br>")
Dim Week
Week = WeekDay(date)
Select Case Week
Case 1
    Response.Write("1")
Case 2
    Response.Write("2")
Case 3
    Response.Write("3")
Case 4
    Response.Write("4")
Case 5
    Response.Write("5")
Case 6
    Response.Write("6")
Case 7
    Response.Write("7")  
End Select
Response.Write("</br>")
Response.Write("For Each  Example")
Dim Num(10)
For i = 0 To 5
    Num(i) = i
Next
Dim j
For Each j In Num
    Response.Write(j & "</br>")
Next

Response.Write(" proccess and function Example" & "</br>")
Sub Res(content)
    Response.Write("Sub test " & content & "</br>")
End Sub
Sub test() ' no parameter function
    Response.Write("</br> i am test </br>")
End Sub
Call Res("i am content")
Res "123"
test ' --no parameter call
Function GetName()
Response.Write "I am GetName Function"
End Function
GetName
Class MyClass 
    Private TestProperty
    Private Sub Class_Initialize()
       Response.Write("</br>Initialize starting!")
       TestProperty = "test"
    End Sub
    Private Sub Class_Terminate()
       Response.Write("Terminate End!")
    End Sub
    Public Property Let SetProperty(ByVal str)
       TestProperty = "</br>" & "change:" & str
    End Property
    Public Property Get GetProperty()
       GetProperty = TestProperty &  "</br>" '返回值
    End Property
End Class
Dim obj
Set obj = New MyClass
obj.SetProperty="123"
Response.Write(obj.GetProperty)
Set obj = nothing '释放对象
%>

 

0x4 开始代码审计之旅(Main)

0x4.1 cms程序结构

command:tree -L 2 -c

├── Announce.Asp
├── cclist.asp
├── conn.asp
├── const.asp
├── count.asp
├── database  // 数据库文件 Access
│   ├── #collection.asa
│   ├── #newasp.asa
│   ├── IPAddress.dat
│   └── maillist.mdb
├── index.asp
├── login.asp
├── runads.asp
├── search.asp
├── showerr.asp
├── skin //仰视文件 忽略
├── vote //投票模块
│   ├── join.asp
│   ├── newaspvote.fla
│   ├── showvote.js
│   ├── vote.asp
│   ├── vote.htm
│   └── vote.swf
├── images //静态文件 忽略
├── inc  //配置文件
│   ├── FlashChannel.asp
│   ├── GetCode.asp
│   ├── NewsChannel.asp
│   ├── NoCheckCode.asp
│   ├── SoftChannel.asp
│   ├── Std_StranJF.Js
│   ├── UploadCls.Asp
│   ├── base64.asp
│   ├── chkinput.asp
│   ├── classmenu.asp
│   ├── cls_custom.asp
│   ├── cls_down.asp
│   ├── cls_editor.asp
│   ├── cls_main.asp
│   ├── cls_md5.asp
│   ├── cls_payment.asp
│   ├── cls_public.asp
│   ├── const.asp
│   ├── email.asp
│   ├── function.asp
│   ├── main.js
│   ├── md5.asp
│   ├── online.asp
│   ├── ubbcode.asp
│   ├── upload.inc
│   └── xslt
├── admin //后台文件
│   ├── Admin_CreateFlash.Asp
│   ├── Admin_UploadFile.Asp
│   ├── admin_admanage.asp
│   ├── admin_article.asp
│   ├── admin_book.asp
│   ├── admin_card.asp
│   ├── admin_channel.asp
│   ├── admin_classad.asp
│   ├── admin_comment.asp
│   ├── admin_confirm.asp
│   ├── admin_helpview.asp
│   ├── admin_link.asp
│   ├── admin_mailist.asp
│   ├── admin_mailout.asp
│   ├── admin_main.asp
│   ├── admin_makenews.asp
│   ├── admin_message.asp
│   ├── admin_online.asp
│   ├── admin_other.asp
│   ├── admin_password.asp
│   ├── admin_paymode.asp
│   ├── admin_probe.asp
│   ├── admin_replace.asp
│   ├── admin_setting.asp
│   ├── admin_special.asp
│   ├── admin_template.asp
│   ├── admin_user.asp
│   ├── admin_vote.asp
│   ├── check.asp
│   ├── Admin_ArticleGather.asp
│   ├── Admin_CreateArticle.Asp
│   ├── Admin_CreateSoft.Asp
│   ├── Admin_SoftGather.asp
│   ├── CleanCache.asp
│   ├── Logdata.asa
│   ├── about.asp
│   ├── admin_account.asp
│   ├── admin_announce.asp
│   ├── admin_articleset.asp
│   ├── admin_bottom.asp
│   ├── admin_classify.asp
│   ├── admin_config.asp
│   ├── admin_conform.asp
│   ├── admin_cookies.asp
│   ├── admin_createindex.asp
│   ├── admin_custom.asp
│   ├── admin_database.asp
│   ├── admin_downfile.asp
│   ├── admin_downlog.asp
│   ├── admin_flash.asp
│   ├── admin_group.asp
│   ├── admin_index.asp
│   ├── admin_jsfile.asp
│   ├── admin_label.asp
│   ├── admin_left.asp
│   ├── admin_loadskin.asp
│   ├── admin_log.asp
│   ├── admin_login.asp
│   ├── admin_logout.asp
│   ├── admin_makeflash.asp
│   ├── admin_makesoft.asp
│   ├── admin_master.asp
│   ├── admin_selfile.asp
│   ├── admin_server.asp
│   ├── admin_soft.asp
│   ├── admin_softerr.asp
│   ├── admin_softset.asp
│   ├── admin_top.asp
│   ├── admin_userorder.asp
│   ├── images
│   ├── remoteupload.asp
│   ├── setup.asp
│   ├── showerr.asp
│   ├── upload.asp
│   └── include
├── js //js文件忽略
├── user //用户模块
│   ├── Upfile.asp
│   ├── Upload.asp
│   ├── activepass.asp
│   ├── addmoney.asp
│   ├── articlelist.asp
│   ├── articlepost.asp
│   ├── changeinfo.asp
│   ├── changepsw.asp
│   ├── check.asp
│   ├── checkreg.asp
│   ├── config.asp
│   ├── confirm.asp
│   ├── downlog.asp
│   ├── favorite.asp
│   ├── flash.asp
│   ├── flashlist.asp
│   ├── flashpost.asp
│   ├── foot.inc
│   ├── friend.asp
│   ├── head.inc
│   ├── help.asp
│   ├── images
│   ├── index.asp
│   ├── login.asp
│   ├── logout.asp
│   ├── main.asp
│   ├── message.asp
│   ├── payment.asp
│   ├── receive.asp
│   ├── reg.asp
│   ├── return.asp
│   ├── sendpass.asp
│   ├── softlist.asp
│   ├── softpost.asp
│   ├── style.css
│   ├── user_style.css
│   ├── usercard.asp
│   ├── userlist.asp
│   └── usersms.asp
├── GuestBook //访客模块
│   ├── check.js
│   ├── config.asp
│   ├── del.asp
│   ├── edit.asp
│   ├── editreply.asp
│   ├── images
│   ├── index.asp
│   ├── post.asp
│   ├── search.js
│   ├── showreply.asp
│   ├── write.asp
│   └── emot
├── Link //链接模块
│   ├── UploadPic
│   ├── addlink.asp
│   ├── delink.asp
│   ├── editlink.asp
│   ├── index.asp
│   ├── link.asp
│   └── link.gif
├── adfile //忽略
├── flash //音频模块
│   ├── GetCode.asp
│   ├── RemoveCache.Asp
│   ├── UploadFile
│   ├── UploadPic
│   ├── comment.asp
│   ├── config.asp
│   ├── down.asp
│   ├── downfile.asp
│   ├── download.asp
│   ├── hits.asp
│   ├── index.asp
│   ├── list.asp
│   ├── play.html
│   ├── rssfeed.asp
│   ├── search.asp
│   ├── show.asp
│   ├── showbest.asp
│   ├── showhot.asp
│   ├── shownew.asp
│   └── special.asp
├── soft //软件模块
│   ├── GetCode.asp
│   ├── RemoveCache.Asp
│   ├── UploadFile
│   ├── UploadPic
│   ├── comment.asp
│   ├── config.asp
│   ├── download.asp
│   ├── error.asp
│   ├── hits.asp
│   ├── index.asp
│   ├── list.asp
│   ├── previewimg.asp
│   ├── rssfeed.asp
│   ├── search.asp
│   ├── show.asp
│   ├── showbest.asp
│   ├── showhot.asp
│   ├── shownew.asp
│   ├── showtype.asp
│   ├── softdown.asp
│   └── special.asp
├── support //介绍模块
│   ├── about.asp
│   ├── about.ini
│   ├── advertise.asp
│   ├── advertise.ini
│   ├── declare.asp
│   ├── declare.ini
│   ├── help.asp
│   ├── help.ini
│   ├── sitemap.asp
│   └── 2.asp
├── api //api模块
│   ├── api.config
│   ├── api_reponse.asp
│   ├── api_user.xml
│   ├── cls_api.asp
│   └── web.config
├── article //文章模块
│   ├── GetCode.asp
│   ├── RemoveCache.Asp
│   ├── UploadFile
│   ├── UploadPic
│   ├── comment.asp
│   ├── config.asp
│   ├── content.asp
│   ├── hits.asp
│   ├── index.asp
│   ├── list.asp
│   ├── rssfeed.asp
│   ├── search.asp
│   ├── sendmail.asp
│   ├── show.asp
│   ├── showbest.asp
│   ├── showhot.asp
│   ├── shownew.asp
│   └── special.asp
├── editor //编辑器模块
│   ├── FCKeditor
│   └── UBBeditor
├── UploadFile //空文件夹
├── web.config // 配置iis的

可以看出来这个经典的asp程序,就是模块化开发的即视感,比如

http://10.211.55.20:8084/soft/就对应着 soft目录下的文件,然后都是单入口模式。

用的最多就是包含数据库链接、用户验证等文件的手段,用来联系整个程序。

0x4.2 审计思路

我个人感觉像asp的cms其实代码量不大,而且结构相当简单,所以我比较喜欢一个一个文件的去读。但是为了提高效率我们可以选择结合功能点去阅读。

先从index.asp文件为开端,code as:

<!--#include file="conn.asp"-->
<!--#include file="const.asp"-->
<!--#include file="inc/cls_public.asp"-->
<%
Dim HtmlFileName, HtmlTemplate 
HTML.ShowIndex(0)
Set HTML = Nothing
CloseConn
%>

文件最开始加载了conn.asp,我们选择跟进看看

<%@ LANGUAGE = VBScript CodePage = 936%> '@指令用来进行一些配置  VBSCRIPT 语法 936代表是gb2312编码
<%
Option Explicit
Dim startime,Conn,db,Connstr
Response.Buffer = True
startime = Timer()
'--定义数据库类别,1为SQL数据库,0为Access数据库
Const isSqlDataBase = 0

Dim NowString, NewAsp, MyAppPath
MyAppPath = ""
'-- 是否开启伪静态功能(False=否,True=是)
Const IsURLRewrite = False
'--系统XML版本设置,最低版本 Const MsxmlVersion=""
Const MsxmlVersion = ".3.0"

If IsSqlDataBase = 1 Then
    '-----------------------SQL数据库连接参数---------------------------------------
    NowString = "GetDate()"
    '--SQL数据库连接参数:数据库名(SqlDatabaseName)、用户名(SqlUsername)、用户密码(SqlPassword)
    '--连接名(SqlLocalName)(本地用(local),外地用IP)
    Const SqlDatabaseName = "newasp"
    Const SqlUsername = "sa"          
    Const SqlPassword = "newasp" 
    Const SqlLocalName = "(local)"
    '-------------------------------------------------------------------------------
Else
    '-----------------------ACCESS数据库连接----------------------------------------
    NowString = "Now()"
    '--ACCESS数据库连接路径;数据库默认在database目录,第一次使用请修改默认数据库名或路径
    '--数据库路径可以使用绝对路径
    db = "database/#newasp.asa"
    '-------------------------------------------------------------------------------
End If

Dim DBPath
'-- 采集数据库连接路径
DBPath = "database/#Collection.asa"

Sub ConnectionDatabase()
    On Error Resume Next
    If IsSqlDataBase = 1 Then
       Connstr = "Provider = Sqloledb; User ID = " & SqlUsername & "; Password = " & SqlPassword & "; Initial Catalog = " & SqlDatabaseName & "; Data Source = " & SqlLocalName & ";"
    Else
       Connstr = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & ChkMapPath(MyAppPath & db)
    End If
    Set Conn = Server.CreateObject("ADODB.Connection")
    Conn.Open Connstr
    If Err Then
       Err.Clear
       Set Conn = Nothing
        Response.Write "数据库连接出错,请打开conn.asp文件检查连接字串。"
       Response.End
    End If
End Sub

Sub CloseConn()
    Set Newasp = Nothing
End Sub
'================================================
' 函数名:ChkMapPath
' 作  用:相对路径转换为绝对路径
' 参  数:strPath ----原路径
' 返回值:绝对路径
'================================================
Function ChkMapPath(ByVal strPath)
    Dim fullPath
    strPath = Replace(Replace(Trim(strPath), "/", "\"), "\\", "\")

    If strPath = "" Then strPath = "."
    If InStr(strPath,":\") = 0 Then
       fullPath = Server.MapPath(strPath)
    Else
       strPath = Replace(strPath,"..\","")
       fullPath = Trim(strPath)
       If Right(fullPath, 1) = "\" Then
           fullPath = Left(fullPath, Len(fullPath) - 1)
       End If
    End If
    ChkMapPath = fullPath
End Function
%>

感觉asp的程序注释写的真的是特别直观,conn.asp主要是数据库链接文件,然后返回一个句柄,我使用的是access数据库,所以我们可以记录下获得的信息:

db = “database/#newasp.asa” //数据库路径 asa 我们可以查看下有没有默认的账户和口令和了解下结构

这些数据库结构对于Access数据库类型的注入来说是绝对必要的,因为access没有元数据表,只能爆破表名。

我们跟进下一个包含的文件const.asp

<!--#include file="inc/cls_main.asp"-->  'Class NewaspMain_Cls 类文件 还有其他类的实现
<!--#include file="inc/function.asp"--> ' 一些方法,感觉是一大堆堆起来的,没必要细读,还是根据功能点去找代码实现效率高点。
<%
Set NewAsp = New NewaspMain_Cls '实例化了对象
NewAsp.ReadConfig '调用了ReadConfig方法
ImagePath = Newasp.InstallDir & "images/" 
%>

然后我们继续跟进下一个包含文件inc/cls_public.asp (代码写的相当长)

 

<!--#include file="classmenu.asp"-->
<%
Dim HTML
Set HTML = New NewaspPublic_Cls
Class NewaspPublic_Cls
    Public CurrentClass,ThisHtmlPath,FirstCalss,ParentClass
    Private Cmd
    
    Private Sub Class_Initialize()
       On Error Resume Next
       Newasp.LoadTemplates 0, 0, 0
    End Sub
'............................. 省略
Public Function ShowIndex(ByVal isHtml)
       Dim HtmlContent
       Newasp.m_intChannelID = 0
       Newasp.LoadTemplates 0, 1, 0
    HtmlContent = Newasp.HtmlContent '载入模版
    HtmlContent = Replace(HtmlContent, "{$ChannelRootDir}", Newasp.InstallDir) '替换标签
       HtmlContent = Replace(HtmlContent, "{$InstallDir}", Newasp.InstallDir)
       If Len(Newasp.HtmlSetting(1)) < 2 Then
           HtmlContent = Replace(HtmlContent, "{$PageTitle}", "首页")
       Else
           HtmlContent = Replace(HtmlContent, "{$PageTitle}", Newasp.HtmlSetting(1))
       End If
       HtmlContent = Replace(HtmlContent, "{$IndexTitle}", "首页")
       HtmlContent = Replace(HtmlContent, "{$ChannelID}", 0)
       HtmlContent = ReadAnnounceContent(HtmlContent, 0)
       HtmlContent = ReadClassMenu(HtmlContent)
       HtmlContent = ReadClassMenubar(HtmlContent)
       HtmlContent = ReadArticlePic(HtmlContent)
       HtmlContent = ReadSoftPic(HtmlContent)
       HtmlContent = ReadArticleList(HtmlContent)
       HtmlContent = ReadSoftList(HtmlContent)
       HtmlContent = ReadFlashList(HtmlContent)
       HtmlContent = ReadFlashPic(HtmlContent)
       HtmlContent = ReadFriendLink(HtmlContent)
       HtmlContent = ReadNewsPicAndText(HtmlContent)
       HtmlContent = ReadSoftPicAndText(HtmlContent)
       HtmlContent = ReadGuestList(HtmlContent)
       HtmlContent = ReadAnnounceList(HtmlContent)
       HtmlContent = ReadPopularArticle(HtmlContent)
       HtmlContent = ReadPopularSoft(HtmlContent)
       HtmlContent = ReadPopularFlash(HtmlContent)
       HtmlContent = ReadStatistic(HtmlContent)
       HtmlContent = ReadUserRank(HtmlContent)
       HtmlContent = Replace(HtmlContent, "{$SkinPath}", Newasp.SkinPath)
       HtmlContent = Replace(HtmlContent, "{$InstallDir}", Newasp.InstallDir)
       If isHtml Then
           ShowIndex = HtmlContent
       Else
           Response.Write HtmlContent
       End If
    End Function

这就是上面最开始 index.asp

<!--#include file="conn.asp"-->
<!--#include file="const.asp"-->
<!--#include file="inc/cls_public.asp"-->
<%
Dim HtmlFileName, HtmlTemplate  
HTML.ShowIndex(0) '这里调用的是 inc/cls_public.asp 类下NewaspPublic_Cls的ShowIndex方法用来渲染首页
Set HTML = Nothing
CloseConn '关闭数据库链接
%>

在这个cms我还想提个关键点:

有个很关键的类(Class NewaspMain_Cls),该类的Newasp实例是这个cms的全局核心对象:

我们需要查看它的构造方法,可以看到通过Cookie设置了很多用户属性,(这些先记下来)

       GetUserip = CheckStr(getIP)
       membername = CheckStr(Request.Cookies(Cookies_Name)("username"))
       memberpass = CheckStr(Request.Cookies(Cookies_Name)("password"))
       menbernickname = CheckStr(Request.Cookies(Cookies_Name)("nickname"))
       membergrade = ChkNumeric(Request.Cookies(Cookies_Name)("UserGrade"))
       membergroup = CheckStr(Request.Cookies(Cookies_Name)("UserGroup"))
       memberclass = ChkNumeric(Request.Cookies(Cookies_Name)("UserClass"))
       memberid = ChkNumeric(Request.Cookies(Cookies_Name)("userid"))
       CheckPassword = CheckStr(Request.Cookies(Cookies_Name)("CheckPassword"))

上面演示了如何对单文件不断回溯从而找到相应的处理类和方法,下面我就是通过这种方法,来通读整个cms,鉴于文章篇幅,下面我会从简表述一些挖掘过程。

0x4.3 SQL注入

关于挖掘asp程序的SQL注入,我们首先通过上文的通读方法,找到关键的过滤函数,如果没有过滤函数,那么就是任意注入啦,如果有过滤函数,我们就有两条路子

1.过滤函数不严谨导致绕过

2.寻找程序猿粗心忘记过滤的可控点

代码有几个过滤函数,分别如下:

C:\Users\xq17\Desktop\wwwroot\inc\cls_main.asp

Public Function CheckBadstr(str) ' 246 line
       If IsNull(str) Then
    CheckBadstr = vbNullString 'str为空则转换为vb的空类型
           Exit Function
       End If
  str = Replace(str, Chr(0), vbNullString) '替换截断字符 
  str = Replace(str, Chr(34), vbNullString) '双引号
       str = Replace(str, "%", vbNullString)
       str = Replace(str, "@", vbNullString)
       str = Replace(str, "!", vbNullString)
       str = Replace(str, "^", vbNullString)
       str = Replace(str, "=", vbNullString)
       str = Replace(str, "--", vbNullString)
       str = Replace(str, "$", vbNullString)
  str = Replace(str, "'", vbNullString) '去掉单引号
       str = Replace(str, ";", vbNullString)
       str = Replace(str, "<", vbNullString)
       str = Replace(str, ">", vbNullString)
  CheckBadstr = Trim(str) '删除字符串两侧的空格,然后返回函数值
    End Function

access数据库没有反斜杠,其他系统可以考虑下,用了这个函数基本大概率没办法注入了。

 

    Public Function ChkNumeric(ByVal CHECK_ID)
  If CHECK_ID <> "" And IsNumeric(CHECK_ID) Then 'IsNumeric 是vbscript的判断,没漏洞
           If CHECK_ID < 0 Then CHECK_ID = 0
      If CHECK_ID > 2147483647 Then CHECK_ID = 0 '防溢出
           CHECK_ID = CLng(CHECK_ID)
       Else
           CHECK_ID = 0
       End If
       ChkNumeric = CHECK_ID
    End Function

Public Function CheckStr(ByVal str)
       If IsNull(str) Then
           CheckStr = ""
           Exit Function
       End If
  str = Replace(str, Chr(0), "")'这个特性能用来绕过关键词
  CheckStr = Replace(str, "'", "''")'这个直接替换单引号为双引号
    End Function

'=============================================================
    '函数名:ChkFormStr
    '作  用:过滤表单字符
    '参  数:str   ----原字符串
    '返回值:过滤后的字符串
    '=============================================================
Public Function ChkFormStr(ByVal str) '这个函数主要防止xss
       Dim fString
       fString = str
       If IsNull(fString) Then
           ChkFormStr = ""
           Exit Function
       End If
       fString = Replace(fString, "'", "&#39;")
       fString = Replace(fString, Chr(34), "&quot;")
       fString = Replace(fString, Chr(13), "")
       fString = Replace(fString, Chr(10), "")
       fString = Replace(fString, Chr(9), "")
       fString = Replace(fString, ">", "&gt;")
       fString = Replace(fString, "<", "&lt;")
       fString = Replace(fString, "&nbsp;", " ")
       ChkFormStr = Trim(JAPEncode(fString))
    End Function
    '=============================================================
    '函数作用:过滤SQL非法字符
    '=============================================================
    Public Function CheckRequest(ByVal str,ByVal strLen)
       On Error Resume Next
       str = Trim(str)
       str = Replace(str, Chr(0), "")
       str = Replace(str, "'", "")
       str = Replace(str, "%", "")
       str = Replace(str, "^", "")
       str = Replace(str, ";", "")
       str = Replace(str, "*", "")
       str = Replace(str, "<", "")
       str = Replace(str, ">", "")
       str = Replace(str, "|", "")
       str = Replace(str, "and", "")
       str = Replace(str, "chr", "")
       str = Replace(str, "@", "")
       str = Replace(str, "$", "")
       
       If Len(str) > 0 And strLen > 0 Then
           str = Left(str, strLen)
       End If
       CheckRequest = str
    End Function

总结下: ChkNumeric CheckStr ChkFormStr(xss) CheckRequest CheckBadstr(xsss) 还有很多其他的方,asp程序比较杂,遇到再细跟就行了,都没办法闭合单引号。

所以我们找注入点也是两个思路:

1.找没有单引号包括的可控语句,且没做类型判断

2.没有进行函数消毒的可控参数进入SQL查询

读完了全部文件,发现了作者对这个程序修修补补的痕迹,对一些历史漏洞点进行重复多次过滤,或者补充过滤(asp程序维护成本高),但是作者一开始的出发习惯还是挺好的,基本都是

SQL = “SELECT TOP 1 * FROM NC_Ca 这样的格式去进行SQL查询,所以作者估计是认真匹配正则然后修补了,所以很遗憾,这个系统我读了2次,还是没找到前台的注入(函数逻辑缝缝补补),又因为是Access数据库,拿到后台注入基本没啥用,除非是那种update的点可能结合getshell来玩下,所以我当时就放弃,欢迎各位师傅继续跟进下这个系统研究一波。

0x4.2 逻辑漏洞

一个纯粹出于学习而发现的无限刷票漏洞(鸡肋且垃圾的洞)

wwwroot\vote\vote.asp

<!--#include file="../conn.asp"-->
<!--#include file="../inc/const.asp"-->
<%
Dim voteid, MyChoose, i, Rs, SQL
If Not IsObject(Conn) Then ConnectionDatabase
voteid = CLng(Request("voteid"))
If Request.Cookies("vote_"&voteid) = "newaspvote_" &voteid Then
    Response.Write("&back=已经参与过投票,谢谢")
    Response.End
Else
    Response.Cookies("vote_"&voteid) = "newaspvote_" &voteid
    Response.Cookies("vote_"&voteid).expires = Date + 3650
    Response.Cookies("vote_"&voteid).domain = Request.ServerVariables("SERVER_NAME")
    MyChoose = Split(Request("myChoose"), ",", -1, 1)
    Set Rs = server.CreateObject("adodb.recordset")
    SQL = "SELECT * FROM NC_Vote WHERE id = " &voteid
    Rs.Open SQL, Conn, 1, 3
    If Not (Rs.BOF And Rs.EOF) Then
       For i = 1 To 5
           If MyChoose(i -1) = "true" Then
              Rs("ChooseNum_"&i&"") = Rs("ChooseNum_"&i&"") + 1
           End If
       Next
       Rs.update
       For i = 1 To 5
           If MyChoose(i -1) = "true" Then
              Conn.Execute ("UPDATE NC_Vote SET VoteNum=VoteNum+1 WHERE id=" &Rs("id"))
           End If
       Next
    End If
    Rs.Close
    Set Rs = Nothing
    Response.Write("&back=投票已经送达,谢谢参与")
End If
CloseConn
%>

Response.Cookies("vote_"&voteid) = "newaspvote_" &voteid 
Response.Cookies("vote_"&voteid).expires = Date + 3650
Response.Cookies("vote_"&voteid).domain = Request.ServerVariables("SERVER_NAME")
'首先投票的成功的话,会设置Cookie的"newaspvote_" &voteid  来代表已经投过票了
If Request.Cookies("vote_"&voteid) = "newaspvote_" &voteid Then
    Response.Write("&back=已经参与过投票,谢谢")
    Response.End
' -- 这个代码是通过cookie匹配来判断的相等则说明已经投过了,但是因为Cookie可控,我们直接burp,去掉这个字段就可以无限刷票了,一般安全的投票是绑定session来限制的。

 

0x4.3 XSS漏洞

这个系统过滤的挺严格的,但是细心找还是能找到几处有意思的xss。

首先是注册的地方:

user/reg.asp code as

ElseIf Newasp.CheckStr(Request("action")) = "reg" Then '34 lines
Call RegNewMember '跟进这个函数
'----------------------------------------
'--- 下面我会一行一行去读然后删掉那些跟可控无关的语句
Sub RegNewMember()
    Dim Rs,SQL
    Dim UserPassWord,strUserName,strGroupName,Password
    Dim rndnum,num1
    Dim Question,Answer,usersex,sex
    On Error Resume Next
  '---------- 这里过滤了input框的所以value,省略
   Set Rs = Newasp.Execute("SELECT username FROM NC_User WHERE username='" & strUserName & "'")
  ' --------- 这里是用户名邮箱唯一性验证,省略
    '-----------------------------------------------------------------
    '系统整合
    '-----------------------------------------------------------------
    Dim API_Newasp,API_SaveCookie,SysKey
    If API_Enable Then
    '---------------------无关代码省略
    '---------------这里是重点---------
    Rs.Close:Set Rs = Nothing
    Set Rs = Server.CreateObject("ADODB.Recordset")
  SQL = "select * from NC_User where (userid is null)'这里打开了NC_User表
    Rs.Open SQL,Conn,1,3
    '------- 下面进行了修改表的操作
    Rs.Addnew
    Rs("username") = strUserName '过滤了
       Rs("password") = Password
    Rs("nickname") = Newasp.CheckBadstr(Request.Form("nickname")) '过滤了
       Rs("UserGrade") = 1
    Rs("UserGroup") = strGroupName '不可控
       Rs("UserClass") = 0
       If CInt(Newasp.AdminCheckReg) = 1 Then
           Rs("UserLock") = 1
       Else
           Rs("UserLock") = 0
       End If
       Rs("UserFace") = "face/1.gif"
       Rs("userpoint") = CLng(Newasp.AddUserPoint)
       Rs("usermoney") = 0
       Rs("savemoney") = 0
       Rs("prepaid") = 0
       Rs("experience") = 10
       Rs("charm") = 10
    Rs("TrueName") = Newasp.CheckBadstr(Request.Form("username")) '过滤了
    Rs("usersex") = usersex '不可控
    Rs("usermail") = Newasp.CheckStr(Request.Form("usermail"))'过滤了
       Rs("oicq") = ""
    Rs("question") = Question '过滤了
       Rs("answer") = md5(Answer)
       Rs("JoinTime") = Now()
       Rs("ExpireTime") = Now()
       Rs("LastTime") = Now()
       Rs("Protect") = 0
       Rs("usermsg") = 0
    Rs("userlastip") = Newasp.GetUserIP ' 这是漏洞点跟进这里
       If CInt(Newasp.AdminCheckReg) = 0 And CInt(Newasp.MailInformPass) = 0 Then
           Rs("userlogin") = 1
       Else
           Rs("userlogin") = 0
       End If
       Rs("UserToday") = "0,0,0,0,0,0,0,0,0,0,0"
       Rs("usersetting") = ",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"
       Rs("ip") = Newasp.GetUserIP
       Rs("Badness") = 0
       Rs("isask") = 0
       Rs.update
       Rs.Close
    '--------------------下面代码省略--------------------

里面有个关键代码:

 

Rs("userlastip") = Newasp.GetUserIP选择跟进这个属性

Path:C:\Users\xq17\Desktop\wwwroot\inc\cls_main.asp

GetUserip = CheckStr(getIP) '跟进CheckStr函数
    Private Function getIP() 
       Dim strIPAddr 
       If Request.ServerVariables("HTTP_X_FORWARDED_FOR") = "" Or InStr(Request.ServerVariables("HTTP_X_FORWARDED_FOR"), "unknown") > 0 Then 
           strIPAddr = Request.ServerVariables("REMOTE_ADDR") 
       ElseIf InStr(Request.ServerVariables("HTTP_X_FORWARDED_FOR"), ",") > 0 Then 
           strIPAddr = Mid(Request.ServerVariables("HTTP_X_FORWARDED_FOR"), 1, InStr(Request.ServerVariables("HTTP_X_FORWARDED_FOR"), ",")-1)
           Actforip = Request.ServerVariables("REMOTE_ADDR")
       ElseIf InStr(Request.ServerVariables("HTTP_X_FORWARDED_FOR"), ";") > 0 Then 
           strIPAddr = Mid(Request.ServerVariables("HTTP_X_FORWARDED_FOR"), 1, InStr(Request.ServerVariables("HTTP_X_FORWARDED_FOR"), ";")-1)
           Actforip = Request.ServerVariables("REMOTE_ADDR")
       Else 
        strIPAddr = Request.ServerVariables("HTTP_X_FORWARDED_FOR")'这里可以控制
           Actforip = Request.ServerVariables("REMOTE_ADDR")
       End If 
    getIP = Replace(Trim(Mid(strIPAddr, 1, 30)), "'", "") '这里没有过滤双引号
    End Function

getIP = Replace(Trim(Mid(strIPAddr, 1, 30)), "'", "")

可以看到这里只是限制了长度为30(完全可以写xss啦),但是没有过滤双引号,这样子就很容易出事情啦,在后台目录下搜索

userlastip这个key寻找输出点。

果断跟进去看看有没有啥过滤的。

Sub EditUser()
    Call PageTop
    Dim userid,username
    userid = Newasp.ChkNumeric(Request("userid"))
    username = Replace(Request("username"), "'", "")
    If userid = 0 Then
       SQL = "SELECT * FROM NC_user WHERE username='" & username & "'"
    Else
       SQL = "SELECT * FROM NC_user WHERE userid=" & userid
    End If
  Set Rs = Newasp.Execute(SQL) '这里直接获取到了sql查询后的对象
    If Rs.BOF And Rs.EOF Then
       FoundErr = True
       ErrMsg = ErrMsg + "<li>Sorry!没有找到任何会员。或者您选择了错误的系统参数!</li>"
       Exit Sub
    End If
  '然后后面直接就是插入了HTMl的input的value,可以看到是双引号的,至此完成xss
  <td class="tablerow1"><input size="30" name="userlastip" value="     <%=Rs("userlastip")%>" type="text" /></td>

过程演示下:

这里可以利用组合拳结合社工来getshell,还有这个系统的cookie里面包含了md5的密码和用户名,代码很简单,自己去读下就知道了。

0x4.4 上传点分析

首先程序的上传点也是分为前台用户和后台用户处的,这里可以进行分析下是否可以进行绕过上传

user 目录下存在

  1. C:\Users\xq17\Desktop\wwwroot\user\Upfile.asp
  2. C:\Users\xq17\Desktop\wwwroot\user\Upload.asp

这两个上传点,下面分析下程序是如何限制和过滤后缀的,然后探讨下绕过的可能性

Upload.asp code as

<!--#include file="setup.asp"--> '不存在
<!--#include file="check.asp"--> ' 跟进这个可以发现,没办法绕过权限判断
<!--#include file="../inc/UploadCls.Asp"-->
<%
'=====================================================================
' 软件名称:新云网站管理系统
' 当前版本:Newasp Site Management System Version 3.0
' 文件名称:upload.asp
' 更新日期:2007-4-2
' 官方网站:新云网络(www.newasp.net www.newasp.cn) QQ:94022511
'=====================================================================
' Copyright 2004-2007 newasp.net - All Rights Reserved.
' newasp is a trademark of newasp.net
'=====================================================================
Server.ScriptTimeOut = 18000
Dim UploadObject,AllowFileSize,AllowFileExt
Dim sUploadDir,SaveFileName,PathFileName,url
Dim sAction,sType,SaveFilePath,UploadPath,m_strInstance,m_intMaxsize
Dim m_strFiletype,m_strType,m_strFileExt,m_strRootPath,m_intshow,m_intRename
Dim ChannelSetting,m_intThumbnail,m_intAutoRename,m_strUploadFileDir,m_strUploadPicDir
'Dim m_intBindDomain,m_strNamedPath
'm_intBindDomain = CInt(Newasp.BindDomain)
'm_strNamedPath = Newasp.NamedPath
'-----   ..............................  减少篇幅,省略了大部分代码
%>

跟进这个check.asp

<%
Dim GroupSetting,Cookies_Name,UserLastIP
Dim rsmember,sqlmember,MemberName,MemberEmail,memberid
MemberName = Newasp.CheckBadstr(Newasp.memberName) '可控
memberid = Newasp.ChkNumeric(Newasp.memberid) '可控
UserLastIP = Newasp.CheckStr(Request.Cookies(Newasp.Cookies_Name)("userlastip")) '可控

If Trim(MemberName) = "" Or memberid = 0 Then
    Response.Redirect ("./login.asp")
End If
MemberName = Left(MemberName,45) '限制名字长度
If Trim(Request.Cookies(Newasp.Cookies_Name)) = "" Then
    Response.Redirect ("./login.asp")
End If
GroupSetting = Split(Newasp.UserGroupSetting(CInt(Newasp.membergrade)), "|||")'可控
Call GetUserTodayInfo
Cookies_Name = "usercookies_" & Newasp.memberid '可控

If Trim(Request.Cookies(Cookies_Name)) = "" Then '跳过
    Response.Cookies(Cookies_Name)("userip") = Newasp.GetUserIP
    Response.Cookies(Cookies_Name)("dayarticlenum") = 0
    Response.Cookies(Cookies_Name)("daysoftnum") = 0
    Response.Cookies(Cookies_Name).Expires = Date + 1
End If

sqlmember = "SELECT userid,UserLock,usermail,userlastip FROM NC_User WHERE username='" & MemberName & "' And UserGrade="& CInt(Newasp.membergrade) &" And userid=" & CLng(memberid)
Set rsmember = Newasp.Execute(sqlmember)
If rsmember.BOF And rsmember.EOF Then '代表是空数据集
    Response.Cookies(Newasp.Cookies_Name) = ""
    Set rsmember = Nothing
  Response.Redirect "login.asp" '跳过这里
    Response.End
Else
    If rsmember("UserLock") > 0 Then
       Response.Cookies(Newasp.Cookies_Name) = ""
       Set rsmember = Nothing
       ErrMsg = "<li>你的用户名已被锁定,你不能登陆!如要开通此帐号,请联系管理员。</li>"
       Call Returnerr(ErrMsg)
       Response.End
    End If
    If Newasp.ChkNumeric(GroupSetting(41)) = 2 Then
       If rsmember("userlastip") <> UserLastIP Or UserLastIP <> Newasp.GetUserIP Then
           Response.Cookies(Newasp.Cookies_Name) = ""
           Set rsmember = Nothing
           ErrMsg = "<li>你已经在其他地方登录,本系统不允许两个人使用同一个帐号登录。</li>"
           Call Returnerr(ErrMsg)
           Response.End
       End If
    End If
    MemberEmail = Trim(rsmember("usermail"))
End If
Set rsmember = Nothing

If CInt(Newasp.memberclass) > 0 Then '可控
    Dim rsUserClass,SQLUserClass
    Set rsUserClass = Server.CreateObject("ADODB.Recordset")
    SQLUserClass = "SELECT userid,UserClass,UserLock,ExpireTime FROM NC_User WHERE username='" & MemberName & "' And userid=" & CLng(Newasp.memberid)
    rsUserClass.Open SQLUserClass,Conn,1,3
    If rsUserClass.BOF And rsUserClass.EOF Then
       Response.Cookies(Newasp.Cookies_Name) = ""
       rsUserClass.Close:Set rsUserClass = Nothing
       Response.Redirect "login.asp"
    Else
       If rsUserClass("UserLock") > 0 Then
           Response.Cookies(Newasp.Cookies_Name) = ""
           rsUserClass.Close:Set rsUserClass = Nothing
           Response.Redirect "login.asp"
       End If
       If DateDiff("D", CDate(rsUserClass("ExpireTime")), Now()) > 0 And rsUserClass("UserClass") <> 999 Then
           rsUserClass("UserClass") = 999
           rsUserClass.Update
       End If
    End If
    rsUserClass.Close:Set rsUserClass = Nothing
End If
%>

这些基本都可以控制,也就是说我们可以绕过验证模块进行上传,我们继续跟进看下上传的流程

这里我整理下对应文件的逻辑:

入口: Upload.asp 一层包含:inc/UploadCls.Asp 二层包含:Upload.inc

Upload.inc :

class: UpFileClass FileInfoClass

UploadCls.Asp:

class: UpFileCls FileInfoCls

强烈推荐看UploadCls.Asp文件内的注释,能帮助我们快速理解变量含义

然后我们重新去读 Upload.asp

Server.ScriptTimeOut = 18000
Dim UploadObject,AllowFileSize,AllowFileExt
Dim sUploadDir,SaveFileName,PathFileName,url
Dim sAction,sType,SaveFilePath,UploadPath
Dim m_strFileExt,m_strRootPath,m_strInstance
UploadObject = CInt(Newasp.UploadClass)   '上传文件对象 --- 0=无组件上传,1=Aspupload3.0组件,2=SA-FileUp 4.0组件 默认是0无组件上传

AllowFileSize = CLng(Newasp.UploadFileSize * 1024 )
AllowFileExt = Newasp.UploadFileType '后台可以修改,存储在数据库的类型
'---- exe|rar|zip|gif|jpg|png|bmp|swf|mid|rm| 默认
AllowFileExt = Replace(Replace(Replace(UCase(AllowFileExt), "ASP", ""), "ASPX", ""), "|", ",") '这里可以进行绕过 AASPSP 这样就可以绕过
url = Split(Request.ServerVariables("SERVER_PROTOCOL"), "/")(0) & "://" & Request.ServerVariables("HTTP_HOST")
'--- url = HTTP://10.211.55.20:8084
sType = UCase(Request.QueryString("sType")) '可控
If Newasp.CheckPost=False Then '限制为post方法
    Call Returnerr(Postmsg)
    Response.End
End If

Dim ChannelSetting,m_strUploadPicDir

If Len(Newasp.Channel_Setting &"") < 30 Then Newasp.Channel_Setting = "0|||1|||2|||3|||4|||0|||1|||UploadPic/|||UploadFile/|||"
ChannelSetting = Split(Newasp.Channel_Setting & "|||||||||||||||", "|||")
m_strUploadPicDir = Replace(Trim(ChannelSetting(7)), "\", "/") ' :UploadPic
If Len(m_strUploadPicDir) < 2 Then m_strUploadPicDir = "UploadPic/"
If Right(m_strUploadPicDir,1) <> "/" Then m_strUploadPicDir = m_strUploadPicDir & "/"

m_strInstance = "content"
  m_strRootPath = Newasp.InstallDir ' /

  Select Case ChannelID ' 可控,这里为1
    Case 0
       If stype = "AD" Then
           UploadPath = "adfile/UploadPic/"
           sUploadDir = Newasp.InstallDir & UploadPath
       ElseIf stype = "LINK" Then
           UploadPath = "link/UploadPic/"
           sUploadDir = Newasp.InstallDir & UploadPath
       Else
           UploadPath = "UploadFile/"
           sUploadDir = Newasp.InstallDir & UploadPath
       End If
    Case Else
       UploadPath = m_strUploadPicDir
       sUploadDir = Newasp.InstallDir & Newasp.ChannelDir & UploadPath
       m_strRootPath = Newasp.InstallDir & Newasp.ChannelDir
End Select

  sAction = UCase(Trim(Request.QueryString("action"))) 'sava
If sAction = "SAVE" Then
    If CInt(Newasp.StopUpload) = 1 Then
       Response.Write ("<script>alert('对不起!本频道未开放上传功能!');history.go(-1)</script>")
       Response.End
    End If
    If CInt(GroupSetting(20)) <> 1 Then
       Response.Write ("<script>alert('对不起!您没有上传文件的权限');history.go(-1)</script>")
       Response.End
    End If
    If CLng(UserToday(1)) => CLng(GroupSetting(21)) Then
       Response.Write ("<script>alert('对不起!您每天只能上传" & GroupSetting(21) & "个文件。');history.go(-1)</script>")
       Response.End
    End If
    Select Case UploadObject
       Case 0,1,2,3
       Call UploadFile  '跟进这个函数 在下面就可以看到了
       Case 999
           Response.Write ("<script>alert('本系统未开放上传功能!');history.go(-1)</script>")
           Response.End
       Case Else
           Response.Write ("<script>alert('本系统未开放上传功能!');history.go(-1)</script>")
           Response.End
    End Select
    Dim strUserToday
    strUserToday = UserToday(0) &","& UserToday(1)+1 &","& UserToday(2) &","& UserToday(3) &","& UserToday(4) &","& UserToday(5)
    UpdateUserToday(strUserToday)
    SaveFilePath = UploadPath & SaveFilePath
    Call OutScript(SaveFilePath)
Else
    Call UploadMain
End If
Sub UploadFile()
    Dim Upload,FilePath,sFilePath,FormName,File,F_FileName
    Dim PreviewSetting,DrawInfo,Previewpath,strPreviewPath
    Dim PreviewName,F_Viewname,MakePreview
    '-- 是否生成缩略图片
    MakePreview = False
    Previewpath = Newasp.InstallDir & Newasp.ChannelDir
    strPreviewPath = m_strUploadPicDir & CreatePath(Previewpath & m_strUploadPicDir)
    PreviewPath = Previewpath & strPreviewpath
    PreviewSetting = Split(Newasp.PreviewSetting, ",")
    If CInt(PreviewSetting(2)) = 1 Then
       DrawInfo = PreviewSetting(5)
    ElseIf CInt(PreviewSetting(2)) = 2 Then
       DrawInfo = Newasp.InstallDir & PreviewSetting(10)
    Else
       DrawInfo = ""
    End If
    If DrawInfo = "0" Then
       DrawInfo = ""
       PreviewSetting(2) = 0
    End If
    sFilePath = CreatePath(sUploadDir) '按日期生成目录
    FilePath = sUploadDir & sFilePath
'-------- FilePath =  /article/UploadPic/2019-8/
'下面开始调用UploadCls.Asp的类了,简单读一下
    Set Upload = New UpFile_Cls
Upload.UploadType = UploadObject              '设置上传组件类型 0
    Upload.UploadPath = FilePath              '设置上传路径
    Upload.MaxSize    = AllowFileSize                 '单位 KB
    Upload.InceptMaxFile = 10                 '每次上传文件个数上限
    Upload.InceptFileType    = AllowFileExt              '设置上传文件限制
    Upload.ChkSessionName    = "uploadPic"
    '预览图片设置
    Upload.MakePreview       = MakePreview
    Upload.PreviewType       = CInt(PreviewSetting(0))       '设置预览图片组件类型
    Upload.PreviewImageWidth = CInt(PreviewSetting(3))       '设置预览图片宽度
    Upload.PreviewImageHeight   = CInt(PreviewSetting(4))       '设置预览图片高度
    Upload.DrawImageWidth       = CInt(PreviewSetting(13))      '设置水印图片或文字区域宽度
    Upload.DrawImageHeight      = CInt(PreviewSetting(14))      '设置水印图片或文字区域高度
    Upload.DrawGraph     = CCur(PreviewSetting(11))      '设置水印透明度
    Upload.DrawFontColor     = PreviewSetting(7)         '设置水印文字颜色
    Upload.DrawFontFamily       = PreviewSetting(8)         '设置水印文字字体格式
    Upload.DrawFontSize      = CInt(PreviewSetting(6))       '设置水印文字字体大小
    Upload.DrawFontBold      = CInt(PreviewSetting(9))       '设置水印文字是否粗体
    Upload.DrawInfo          = DrawInfo           '设置水印文字信息或图片信息
    Upload.DrawType          = CInt(PreviewSetting(2))       '0=不加载水印 ,1=加载水印文字,2=加载水印图片
    Upload.DrawXYType    = CInt(PreviewSetting(15))     '"0" =左上,"1"=左下,"2"=居中,"3"=右上,"4"=右下
    Upload.DrawSizeType      = CInt(PreviewSetting(1))       '"0"=固定缩小,"1"=等比例缩小
    If PreviewSetting(12)<>"" Or PreviewSetting(12)<>"0" Then
       Upload.TransitionColor   = PreviewSetting(12)        '透明度颜色设置
    End If
    '执行上传
'--------- 调用了SaveUpFile方法 我们跟进看看
    Upload.SaveUpFile
    If Upload.ErrCodes<>0 Then
'----------这个点就是报错什么类型不对啥的点 --------
       Response.write ("<script>alert('错误:"& Upload.Description & "');history.go(-1)</script>")
       Exit Sub
    End If
'至少一个
    If Upload.Count > 0 Then
       For Each FormName In Upload.UploadFiles
           Set File          = Upload.UploadFiles(FormName)
           SaveFilePath      = sFilePath & File.FileName
           F_FileName        = FilePath & File.FileName
           m_strFileExt      = File.FileExt
           '创建预览及水印图片
           If Upload.PreviewType<>999 and File.FileType=1 then
              PreviewName = "p" & Replace(File.FileName,File.FileExt,"") & "jpg"
              F_Viewname = Previewpath & PreviewName
              '创建预览图片:Call CreateView(原始文件的路径,预览文件名及路径,原文件后缀)
              Upload.CreateView F_FileName,F_Viewname,File.FileExt
              If CBool(MakePreview) Then
                  Call OutPreview(strPreviewPath & PreviewName)
              End If
           End If
           Set File = Nothing
       Next
    Else
       Call OutAlertScript("请选择一个有效的上传文件。")
       Exit Sub
    End If
    Set Upload = Nothing
End Sub

Sub UploadMain()
    Dim PostRanNum
    Randomize
    PostRanNum = Int(900*rnd)+1000
    Session("uploadPic") = Cstr(PostRanNum)
%>

读完可以确定Upload.SaveUpFile这个是重点方法,我们选择跟进看看

 

Public Sub SaveUpFile()
       On Error Resume Next
       Select Case (Upload_Type) 
           Case 0
              ObjName = "无组件"
              Set UploadObj = New UpFile_Class
              If Err.Number<>0 Then
                  ErrCodes = 1
              Else
           SaveFile_0 '跟进这个
              End If
           Case 1
    '..................................省略无关代码
       
    End Sub

Private Sub SaveFile_0()
       Dim FormName,Item,File
       Dim FileExt,FileName,FileType,FileToBinary
       UploadObj.InceptFileType = InceptFile
       UploadObj.MaxSize = FileMaxSize
       UploadObj.GetDate () '取得上传数据
       FileToBinary = Null
  '-------- 这里是设置ErrCodes的关键代码,后面会进行判断必须要为0,初始值为0
  ' 在这里是对应的ErrorCodes对应的错误信息
  'Public Property Get Description
    '   Select Case ErrCodes
    '      Case 1 : Description = "不支持 " & ObjName & " 上传,服务器可能未安装该组件。"
    '      Case 2 : Description = "暂未选择上传组件!"
    '      Case 3 : Description = "请先选择你要上传的文件!"
    '      Case 4 : Description = "文件大小超过了限制 " & (FileMaxSize\1024) & "KB!"
    '   Case 5 : Description = "文件类型不正确!"
    '      Case 6 : Description = "已达到上传数的上限!"
    '      Case 7 : Description = "请不要重复提交!"
    '      Case Else
    End Property
       If Not IsEmpty(SessionName) Then ' SessionName = uploadPic
           If Session(SessionName) <> UploadObj.Form(SessionName) or Session(SessionName) = Empty Then
              ErrCodes = 7
              Exit Sub
           End If
       End If
       
       IsRename = ChkBoolean(UploadObj.Form("Rename"))
       If IsMakePreview Then
           m_blnNoThumbnail = ChkBoolean(UploadObj.Form("NoThumbnail"))
           If m_blnNoThumbnail Then
              IsMakePreview = False
           Else
              IsMakePreview = True
           End If
       End If
       
       If UploadObj.Err > 0 then
           Select Case UploadObj.Err
              Case 1 : ErrCodes = 3
              Case 2 : ErrCodes = 4
              Case 3 : ErrCodes = 5
           End Select
           Exit Sub
       Else
           For Each FormName In UploadObj.File    ''列出所有上传了的文件
              If Count>MaxFile Then
                  ErrCodes = 6
                  Exit Sub
              End If
              
              Set File = UploadObj.File(FormName)
              FileExt = FixName(File.FileExt) '后缀replace
              If CheckFileExt(FileExt) = False then '后缀判断
                  ErrCodes = 5
                  EXIT SUB
              End If
              If Not IsRename Then
                  FileName = FormatName(FileExt)
              Else
                  FileName = File.FileName
              End If
              FileType = CheckFiletype(FileExt) '0
              If IsBinary Then
                  FileToBinary = File.FileData
              End If
              If File.FileSize>0 Then
                  File.SaveToFile Server.Mappath(FilePath & FileName) '然后这里进行了保存,跟进这个
                  AddData FormName , _ 
                         FileName , _
                         FilePath , _
                         File.FileSize , _
                         File.FileType , _
                         FileType , _
                         FileToBinary , _
                         FileExt , _
                         File.FileWidth , _
                         File.FileHeight
                  Count = Count + 1
                  CountSize = CountSize + File.FileSize
                  FileMaxSize = CCur(CountSize)
              End If
              Set File=Nothing
           Next
           For Each Item in UploadObj.Form
              If UploadForms.Exists (Item) Then _
                  UploadForms(Item) = UploadForms(Item) & ", " & UploadObj.Form(Item) _
              Else _
              UploadForms.Add Item , UploadObj.Form(Item)
           Next
           If Not IsEmpty(SessionName) Then Session(SessionName) = Empty
       End If
    End Sub

这里有三个关键的check方法(都是根据ext来搞的)

 

    Private Function FixName(Byval UpFileExt)
       If IsEmpty(UpFileExt) Then Exit Function
       FixName = Lcase(UpFileExt)
       FixName = Replace(FixName,Chr(0),"") '这里干掉了字符
       FixName = Replace(FixName,".","")
       FixName = Replace(FixName,"'","")
       FixName = Replace(FixName,"asp","")
       FixName = Replace(FixName,"asa","")
       FixName = Replace(FixName,"aspx","")
       FixName = Replace(FixName,"cer","") '这里可以进行一下绕过ccerer
       FixName = Replace(FixName,"cdx","")
       FixName = Replace(FixName,"htr","")
    End Function

Private Function CheckFileExt(FileExt)
       Dim Forumupload,i
       CheckFileExt=False
       If FileExt="" or IsEmpty(FileExt) Then
           CheckFileExt = False
           Exit Function
       End If
       If FileExt="asp" or FileExt="asa" or FileExt="aspx" Then '这里直接白名写死了
           CheckFileExt = False
           Exit Function
       End If
       Forumupload = Split(InceptFile,",")
       For i = 0 To ubound(Forumupload)
           If FileExt = Trim(Forumupload(i)) Then
              CheckFileExt = True
              Exit Function
           Else
              CheckFileExt = False
           End If
       Next
    End Function
    Private Function CheckFiletype(Byval FileExt) '返回类型
       FileExt = Lcase(Replace(FileExt,".",""))
       Select Case FileExt
              Case "gif", "jpg", "jpeg","png","bmp","tif","iff"
                  CheckFiletype=1
              Case "swf", "swi"
                  CheckFiletype=2
              Case "mid", "wav", "mp3","rmi","cda"
                  CheckFiletype=3
              Case "avi", "mpg", "mpeg","ra","ram","wov","asf"
                  CheckFiletype=4
              Case Else
                  CheckFiletype=0
       End Select
    End Function

              FileExt = FixName(File.FileExt) '后缀replace
              If CheckFileExt(FileExt) = False then '后缀判断
                  ErrCodes = 5
                  EXIT SUB
              End If
' -------------- FixName(替换为空) +  CheckFileExt(后台可设置任意类型) => 导致绕过限制cer

File.SaveAs Server.Mappath(FilePath & FileName)
AddData File.Name , _  ' 换行符 类似python的 \
              FileName , _
              FilePath , _
              File.Size , _
              File.ContentType , _
              FileType , _
              FileToBinary , _
              FileExt , _
              File.ImageWidth , _
              File.ImageHeight

    Private Sub AddData( Form_Name,File_Name,File_Path,File_Size,File_ContentType,File_Type,File_Data,File_Ext,File_Width,File_Height )
       Set FileInfo = New FileInfo_Cls
           FileInfo.FormName = Form_Name
           FileInfo.FileName = File_Name
           FileInfo.FilePath = File_Path
           FileInfo.FileSize = File_Size
           FileInfo.FileType = File_Type
           FileInfo.FileContentType = File_ContentType
           FileInfo.FileExt = File_Ext
           FileInfo.FileData = File_Data
           FileInfo.FileHeight = File_Height
           FileInfo.FileWidth = File_Width
           UploadFiles.Add Form_Name , FileInfo
       Set FileInfo = Nothing
    End Sub

AddData这个方法是设置那个对象的一些属性。

我们跟进这个最后保存的方法看看:

Set UploadObj = New UpFile_Class
Set File = UploadObj.File(FormName) '生成File对象的过程
Server.CreateObject ("Scripting.Dictionary") 'ActiveX对象实例

所以最后调用的就是:

    Public Sub SaveToFile (Byval Path)
       Dim Ext,oFileStream
       Ext = LCase(Mid(Path, InStrRev(Path, ".") + 1)) '取后缀
       If Ext <> FileExt Then Exit Sub '这里的话进行了二次验证
       If Trim(Path)="" or FileStart=0 or FileName="" or Right(Path,1)="/" Then Exit Sub
       ' 上面就是一些常规判断
       If InStr(1, Path, ".asp", 1) > 0 Then Exit Sub
       '从第一个字符开始搜索 采取文本比较.asp在path的位置 vbscript都是从1开始的要区别开来
       'On Error Resume Next
       Set oFileStream = CreateObject ("Adodb.Stream")'刘属性
       oFileStream.Type = 1
       oFileStream.Mode = 3
       oFileStream.Open
       oUpFileStream.Position = FileStart
       oUpFileStream.CopyTo oFileStream,FileSize
       oFileStream.SaveToFile Path,2 '读取流开始(长度)存到指定路径
       oFileStream.Close
       Set oFileStream = Nothing 
    End Sub

上面就是上传文件的步骤,下面看看怎么返回上传的路径:

Call OutScript(SaveFilePath)

同样是这个文件的后面一些输出方法和一些没啥关系的输出(这个大概看看就可以知道路径是怎么返回的):

可以发现和上面是对应的。

0x4.5 后台getshell

0x 4.5.1 备份数据库getshell

这个应该可以说是很经典的漏洞了,操作相当简单,但是我们可以从代码层面进行解读一下。

  1. 上传图片马,获取到路径
  2. 把图片马通过备份功能修改为asp后缀

根据抓包得到url /admin/admin_database.asp?action=BackupData&act=Backup,单入口我们直接跟进这个文件就行了。

 

Dim bkfolder, bkdbname, fso, fso1
Dim Action
Action = LCase(Request("action"))

Select Case Action
    Case "renamedata" '数据库更名
       If Not ChkAdmin("RenameData") Then
           Server.Transfer("showerr.asp")
           Request.End
       End If
       Call RenameData()
Case "backupdata" '备份数据 进入这里
If Not ChkAdmin("BackupData") Then '验证权限
           Server.Transfer("showerr.asp")
           Request.End
       End If
       If request("act") = "Backup" Then
       If IsSqlDataBase = 1 Then
              Call BackupSqlDatabase()
       Else
       Call BackupDatabase()'调用过程
       End If
' -------- 过程代码如下

Sub BackupDatabase()
  Dbpath = request.Form("Dbpath") '获取可控的数据文件路径
    If InStr(Dbpath, ":") = 0 Then
       Dbpath = Server.MapPath(Dbpath)
    Else
    Dbpath = Dbpath 
    End If
    bkfolder = request.Form("bkfolder")
    bkdbname = request.Form("bkdbname")
  Set Fso = server.CreateObject("scripting.filesystemobject") '创建Fso对象
  If fso.FileExists(dbpath) Then '判断dbpath路径是否存在
    If CheckDir(bkfolder) = True Then 'CheckDir是判断是否存在目录
      fso.CopyFile dbpath, bkfolder& "\"& bkdbname '这里直接把dbpath文件copy到了控制的bkdbname
       Else
           MakeNewsDir bkfolder
           fso.CopyFile dbpath, bkfolder& "\"& bkdbname
       End If
       Succeed("备份数据库成功,您备份的数据库路径为" &bkfolder& "\"& bkdbname)
    Else
       FoundErr = True
       ErrMsg = "找不到您所需要备份的文件。"
       Exit Sub
    End If
End Sub

所以成因非常简单,一般防御这种,可以直接把数据库路径写死,但是也不安全,特别是asa这种后缀数据库,所以最好用白名单去限制备份的后缀。

0x4.5.2 上传getshell的突破

经过上面分析,核心上传代码还是那两个包含文件,所以前台和后台上传文件虽然不一样,但是过滤是一样的,那么能不能突破这个上传呢,我这里简单说下,代码非常暴力的多次遇到asp就exit了,所以说没有办法,但是有时候可以利用解析漏洞等做点事情。

0x4.5.3 插入配置文件shell

上面其实我都是列举了经典的asp程序getshell思路,这个系统把配置文件都是存入了数据库,所以没有办法进行getshell,一般只要不过滤单引号,然后把网站设置这些写入了类似config.asp等文件的就可以getshell,下次可以分析一波这种系统的漏洞,如何闭合写asp一句话也是一种技巧。

 

0x5 关于这个系统的碎碎念

很遗憾没有挖掘到比较好的漏洞,但是我觉得有时候展示自己失败的和第一次尝试的过程,对新手会更有启发,代码审计其实更多是体力活,需要耐心和重复审计,后面如果有机会等进一步提高了asp代码审计的能力,我会再回溯这个系统。

 

0x6 感想(Thought)

第一次接触新语言的的代码审计,我个人还是推崇自己先静态读一次,这样能快速增加自己对新语言的熟悉感,所以这个cms我基本是逐行地去读,虽然说速度挺慢的,但是能让我更好地去理解开发者的心思,也更容易发现脆弱点。因为已经熟悉了asp程序的结构,后面挖掘其他cms的话,为了提高效率,会进行动态审计。由于最近在进修一些关于红队的知识,也没有HW等一些实战机会了,估计比较难遇到一些有趣的asp程序了,不过我接下来这几天可以记录下自己第一次做红队时从信息收集->撕开小口子->自闭->钓鱼攻击->免杀及其邮件伪造的文章跟各位师傅探讨下。

 

0x6 参考链接(Refer Link)

Web.config设置system.webServer

ASP入门(十六)-ASP开发的规范

(完)