|
网络安全编程:开发后台登录地址扫描工具[url=]计算机与网络安全[/url] 昨天
一次性付费进群,长期免费索取资料。
回复公众号:微信群 可查看进群流程。
本文来完成一个后台扫描工具。在进行Web安全活动时通常会去查找后台登录页面,找到登录页面以后可能会使用暴力破解密码或者SQL注入等一系列的方法去尝试登录。
对于一个扫描后台登录地址的工具来说,常用的方法是通过字典去尝试。这里使用字典中保存的后台页面来逐个地进行尝试,不过后台登录页面通常都有一些俗称的命名方式,虽然没有规范,但是大多都使用诸如“login”、“index”这类的文件名,相比密码而言它的组合就少许多了。
下面看一下扫描后台工具的界面,界面如图1所示。
图1 后台登录扫描工具
图1中最常见的编辑框用于输入要扫描网站的URL,下拉框用于选择扫描的类型,这里扫描的类型分为“PHP”和“ASP”两种。填入扫描网站的URL,并选择相应的扫描类型单击“扫描”按钮就可以开始进行扫描,扫描的结果会显示在程序下方的列表框中。
在程序启动的时候,需要在窗口初始化时初始化程序下方的列表框,代码如下:
- m_ScanList.InsertColumn(0, "扫描结果");
- m_ScanList.SetColumnWidth(0, 400);
复制代码 m_ScanList是与CListCtrl关联的控件变量,相关的定义包括:
- CListCtrl m_ScanList; // 列表框控件变量
- HANDLE m_hEvent; // 事件变量,用于多线程传递参数时使用
- afx_msg void OnBnClickedButton1();
- // 扫描线程
- static DWORD WINAPI ScanThread(LPVOID lpParam);
- // 检查 URL 是否有效,即检查登录后台页面是否有效
- BOOL CheckUrl(CString strUrl);
复制代码 填写好URL地址和选择了下拉列表以后,单击“扫描”按钮就可以开始扫描了,由于扫描任务不能和程序的主程序为同一个线程,那么就需要再启动一个线程启动扫描任务,“扫描”按钮的代码如下:
- void CScanAdminPageDlg::OnBnClickedButton1()
- {
- // TODO: 在此添加控件通知处理程序代码
- // 清除列表框内容
- m_ScanList.DeleteAllItems();
- // 获取输入的 URL 内容
- CString strURL;
- GetDlgItemText(IDC_EDIT1, strURL);
- // 创建事件
- m_hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
- // 创建扫描线程
- HANDLE hThread = NULL;
- hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ScanThread, this, 0, NULL);
- WaitForSingleObject(m_hEvent, INFINITE);
- ResetEvent(m_hEvent);
- }
复制代码 单击“扫描”按钮时会创建扫描线程来进行扫描后台,线程函数的函数名为ScanThread。代码如下所示:
- DWORD WINAPI CScanAdminPageDlg::ScanThread(LPVOID lpParam)
- {
- CScanAdminPageDlg *pThis = (CScanAdminPageDlg*)lpParam;
- SetEvent(pThis->m_hEvent);
- // 在线程函数中获取下拉选框的内容
- // 下拉选项是 asp 和 php 两项
- CString strWebType;
- pThis->GetDlgItemText(IDC_COMBO1, strWebType);
- // 在线程函数中获取扫描 URL 地址
- CString strUrl;
- pThis->GetDlgItemText(IDC_EDIT1, strUrl);
- // 通过下拉选项来构造字典
- // asp.dic 或 php.dic
- char szFileName[MAX_PATH] = { 0 };
- wsprintf(szFileName, "%s.dic", strWebType);
- // 打开字典文件
- // 从中读取可能的后台页面
- FILE *DicFile = NULL;
- fopen_s(&DicFile, szFileName, "r");
- char szDic[MAXBYTE] = { 0 };
- while ( fgets(szDic, MAXBYTE, DicFile) )
- {
- if ( szDic[lstrlen(szDic) - 1] == '\n' )
- {
- szDic[lstrlen(szDic) - 1] = NULL;
- }
- // 扫描 URL 地址和字典中的页面文件进行拼接
- CString strCheckUrl = strUrl + szDic;
- // 判断页面是否存在
- // 存在则在地址的结尾增加“[OK]”字样
- if ( pThis->CheckUrl(strCheckUrl) )
- {
- strCheckUrl += "[OK]";
- pThis->m_ScanList.InsertItem(0, strCheckUrl);
- continue;
- }
- // 将扫描的地址添加至字典
- pThis->m_ScanList.InsertItem(pThis->m_ScanList.GetItemCount(), strCheckUrl);
- }
- return 0;
- }
复制代码 下拉框选项中有“asp”和“php”两项,分别是常见的关于ASP系统和PHP系统后台登录页面的文件名,这些文件名可以从其他的安全工具中获取,也可以自己收集。这里将ASP和PHP的字典文件分别命名为“asp.idc”和“php.dic”,在扫描线程函数中通过获取下拉选项“asp”或“php”与“.dic”字符串进行拼接,从而合并成完整的字典的名称。打开字典以后逐行地读取页面文件,读取的页面文件类似“admin/login.asp”“wp-login.php”等,将扫描的URL地址和字典文件进行拼接后,构成一个完整的扫描页面进行测试,如果该页面文件存在,则在后面拼接“[OK]”字符串表示后台页面存在。最后,无论页面文件是否存在都会添加到程序的列表框中。
在程序中,判断页面文件是否存在的函数是CheckUrl函数,该函数的代码如下:
- BOOL CScanAdminPageDlg::CheckUrl(CString strUrl)
- {
- // 建立一个 SESSION
- CInternetSession session("ScanAdminPage");
- // 建立一个 HTTP 连接
- CHttpConnection *pServer = NULL;
- // 获取一个 HTTP 文件
- CHttpFile *pFile = NULL;
- // 检测输入的 URL 是否符合格式,并把 URL 解析
- CString strServerName; // 服务器地址
- CString strObject; // URL 指向的对象
- INTERNET_PORT nPort; // 端口号
- DWORD dwServiceType; // 服务类型
- // URL 解析失败则返回
- // 解析扫描的 URL
- if ( !AfxParseURL(strUrl, dwServiceType, strServerName, strObject, nPort) )
- {
- return NULL;
- }
- // 服务类型错误
- if ( dwServiceType != INTERNET_SERVICE_HTTP )
- {
- return NULL;
- }
- // 配置连接服务器的地址、端口,并获取该 HTTP 连接
- pServer = session.GetHttpConnection(strServerName, nPort);
- // 打开该 HTTP 连接
- pFile = pServer->OpenRequest(CHttpConnection::HTTP_VERB_GET, strObject, NULL, 1,
- NULL, NULL, INTERNET_FLAG_EXISTING_CONNECT | INTERNET_FLAG_NO_AUTO_REDIRECT);
- try
- {
- // 发起请求
- pFile->SendRequest();
- }
- catch (CException* e)
- {
- return NULL;
- }
- DWORD dwRet;
- // 获取该请求的回应状态码
- pFile->QueryInfoStatusCode(dwRet);
- BOOL bRet = FALSE;
- // HTTP 返回值为 200 表示成功
- if ( dwRet == 200 )
- {
- bRet = TRUE;
- }
- // 释放指针
- if ( pFile != NULL )
- {
- delete pFile;
- }
- if ( pServer != NULL )
- {
- delete pServer;
- }
- // 关闭会话
- session.Close();
- return bRet;
- }
复制代码
在代码中CInternetSession类方法GetHttpConnection是用来使程序和服务器建立会话的,这步算是建立了一个TCP的连接,并没有真正地与Web进行通信。使用CInternetSession:: GetHttpConnection方法建立会话后返回了CHttpConnection类的指针,通过CHttpConnection类方法OpenRequest来发起HTTP请求,这部分就是HTTP的真正操作了,可以发起POST或GET等常用的HTTP请求。ChttpConnection::OpenRequest方法发起HTTP请求后就返回了CHttpFile指针,通过CHttpFile类方法QueryInfoStatusCode来获取服务器的返回码。如果返回码是200,表示页面存在,如果返回其他值则表示页面不存在。
Web通信最常使用的就是HTTP协议,出于安全的考虑又发展了HTTPS协议。通过浏览器来浏览网页时就是通过HTTP协议来发起GET或POST请求,然后Web服务器会将响应和结果返回给浏览器。浏览器在向Web服务器发起HTTP请求时通常被称为request,Web服务器返回数据给浏览器时被称为response。HTTP协议也有它相关的头部,通过CHttpFile类的QueryInfoStatusCode可以获得服务器的返回码,这里给出MSDN中对于HTTP返回值的描述,如表1所示,常见的返回值如表2所示。
表1 HTTP状态码分组
表2 常用的HTTP状态码
下面用编写的扫描工具来扫描DVWA的后台页面地址,虽然打开DVWA系统后直接可以显示它的登录页面,但这里只是用来进行演示,如图2所示。
图2 扫描DVWA后台的登录页面
|
|