界面的布局
这种工具几乎所有语言都可以完成,首选语言应该是 Python 居多,因为 Python 处理 HTTP 的库容易使用,上手更快。不过,我这里使用的是 VC 中的 MFC 来实现的。先来看看它的界面,界面如下:
在界面上,URL 后面的编辑框属于要扫描的 URL 地址,后面的下拉框选择对应的语言,比如 PHP、ASP 等。填好 URL 地址和对应的语言后,点击扫描就开始进行扫描。扫描的进度和结果会出现在下面的列表框中的。
代码编写
整个代码都是通过点击按钮开始的,那么就从点击按钮后触发点击事件来写代码,点击按钮的代码如下:
void CScanAdminPageDlg::OnBnClickedButton1()
{
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);
}
在按钮点击后,通过 CreateThread 来创建一个线程进行扫描。
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;
}
上面的代码中,关键的部分是 CheckUrl 函数,它用来请求实际的 URL,并检查 HTTP 的请求状态。
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;
}
程序测试
在虚拟机中启动 DVWA 靶场,然后填入靶场的地址,选择的语言是 PHP,因为 DVWA 是 PHP 编写的,然后点击扫描。可以看到,其登录页面是 login.php。
完整内容参考《C++黑客编程揭秘与防范》(第三版)一书。