根林大老虎 发表于 2023-6-22 01:51:14

地图Vscript注入漏洞汇总(2023.06.24)

本帖最后由 根林大老虎 于 2023-6-25 21:18 编辑



此贴使用UB社区论坛的Markdown模式编辑,使用电脑端浏览可以获得更好的阅读体验。

在2023年6月24日贴主和Luffaren的交流中,新发现以下三张地图存在Vscript注入漏洞:

`ze_japans_3rd_bombing_v8` `ze_noob_too_easy_v3_3a` `ze_ronald_v2`

Luffaren还给出了GFL社区的漏洞补丁文件作为参考(见正文部分**6. 修复方法**)。

漏洞详细信息请浏览**漏洞存在情况概要**和**正文**部分。

-----


### **漏洞存在情况概要**


| 编号 |                  地图名                     |                     存在漏洞的文件名                     | 存在漏洞的函数名                                             |
| :--: | :-------------------------------------------: | :----------------------------------------------------------: | :----------------------------------------------------------- |
|1   |       ze_eternal_grove_v3<br />永恒之森       | \scripts\vscripts\luffaren\mapmanager\mapmanager_events.nut| OnGameEvent_player_connect_raw<br />OnGameEvent_player_say_raw<br />OnGameEvent_player_changename_raw<br />OnGameEvent_player_disconnect_raw |
|2   |      ze_best_korea_v3<br />最美朝鲜         | \scripts\vscripts\luffaren\mapmanager\mapmanager_events.nut| 与1相同                                                      |
|3   |         ze_pizzatime_v9<br />披萨时间         | \scripts\vscripts\luffaren\eventmanager\\eventmanager_events.nut | 与1相同                                                      |
|4   | ze_touhou_gensokyo_o4<br />幻想乡:阿尔法突袭 | \scripts\vscripts\koyomaple\gensokyo\eventmanager_events.nut | OnGameEvent_player_connect_raw<br />OnGameEvent_player_say_raw |
|5   |ze_drakelord_castle_b3<br />龙爵的移动城堡   | \scripts\vscripts\luffaren\eventmanager\\eventmanager_events.nut | 与1相同                                                      |
|6   |      ze_crazykart_v4<br />卤粉卡丁车      |            \scripts\vscripts\luffaren\manager.nut            | **M_chat**<br />文件第4961行:<br />`EntFireByHandle(self,"RunScriptCode"," TextSign(\""+pt+"\",7.5,255,255,255) ",0.00,pclass.handle,pclass.handle);`<br />当地图VIP玩家发出聊天框信息 "!vipsign XXXXXX"时,信息中的"XXXXXX"会赋值到参数pt,引发注入漏洞。 |
|7   |         ze_japans_3rd_bombing_v8            |         \scripts\vscripts\2554\mapmanager_events.nut         | 与1相同                                                      |
|8   |            ze_noob_too_easy_v3_3a             |         \\scripts\vscripts\mapmanager_events.nut         | OnGameEvent_player_connect_raw                               |
|9   |               ze_ronald_v2                  |                                                            | 与1相同                                                      |

------

### **正文**


​      2023年4月12日,贴主于凌晨发现一个关于游戏CS:GO地图Vscript脚本的严重地图漏洞,该漏洞在ze地图中应该是第一次被发现。此漏洞影响十分严重,建议各位地图作者与服务器管理员务必检查自己或服务器地图池的地图中有无此类漏洞,有则迅速修补。也建议服务器管理员暂时对有漏洞的地图进行下架处理,在漏洞修补后再进行上传,避免服务器运行受到影响。

​      视频:[【给服务器一点小小的权限震撼】]( https://www.bilibili.com/video/BV1Dj411c7SR/?share_source=copy_web&vd_source=7f0c8d4962d6d4cfa4085779103c5671)

#### 1. 漏洞类型

​      CS:GO地图Vscript脚本注入漏洞。

#### 2. 漏洞危害

​      地图中存在此漏洞时,任意玩家可以使用此漏洞在服务器地图中执行任意Vscript脚本语句、对实体操作(任意触发实体IO或增删实体)、使地图中玩家执行部分客户端控制台指令、执行部分服务器控制台指令、使服务端崩溃、越权使用部分管理员指令等。

#### 3. 漏洞原理

​      测试地图:ze_pizzatime_v9(披萨时间)

​      这张地图中使用了监听 `player_say` 事件的监听器实体。该实体在监听到玩家发送聊天框消息的 `player_say` 事件后,会自动执行Vscript脚本中的特定函数,所执行的函数的定义及注释如下:

```squirrel
// 语言:Squirrel 2.2.3 stable

// 监听器监听到player_say事件后会执行此函数,其中参数event_data由监听器传入,是一张Squirrel表(Squirrel语言的基本数据类型)
// 参数event_data的表中含有此次监听到的事件的信息,比如发送聊天框消息的玩家的用户ID,发送的消息内容等
::OnGameEvent_player_say_raw <- function(event_data)
{

      // 这里定义了一个变量code,该变量的值为由若干个字符串和事件信息拼接而成的字符串
      // event_data.userid的值为发送聊天框消息的玩家的用户ID
      // event_data.text的值为发送的消息内容

      local code = " OnPlayerChat(" + event_data.userid + ",\"" + event_data.text + "\"); ";


      // 这里执行了EntFire()函数,该函数的功能为触发一个实体的输入(Input)
      // 其中,"event_manager"是目标实体的targetname
      // "RunScriptCode"是输入的类型,该输入类型的作用为将参数code字符串视作一行Vscript语句,进行编译和执行
      // 0.00和null这两个参数我们可以不用管

      EntFire("event_manager", "RunScriptCode", code, 0.00, null);
}
```

​      **简单来说**,就是每当有一个玩家在聊天框发送消息,就会有以下格式的**字符串**会**被当作Vscript代码编译和执行**:

```squirrel
OnPlayerChat(玩家用户ID,"发送的消息内容");
```
​      其中`OnPlayerChat()`是一个定义在其它地方的函数,作用是根据玩家ID和发送消息内容执行一些指令,具体定义内容可以不用管。

​      先举个正常的例子,如果一名玩家(假定用户ID为1)在聊天框中发送了以下消息:

> **能不能给我订张格里斯?**

​      那么监听器将监听到 `player_say` 事件,自动执行 `OnGameEvent_player_say_raw()` 函数。而在此函数的此次执行过程中,参数 `code` 被构建成一个这样的字符串:

```squirrel
OnPlayerChat(1,"能不能给我订张格里斯?");
```

​      以上字符串会在 `EntFire()` 函数中,在 `"RunScriptCode" `的作用下被编译并执行。此次编译执行十分正常,并没有什么不妥。

​      **但是**,如果一名玩家(假定用户ID为2)在聊天框中发送了以下消息:

> **" + print("xxx狗都不玩") + "**

​    那么参数 `code` 构建的字符串如下所示:

```squirrel
OnPlayerChat(2,"" + print("xxx狗都不玩") + "");
```

​      在 `"RunScriptCode"` 编译并执行以上 `code` 时,可以发现, `OnPlayerChat()` 函数的第二个参数的形式与之前玩家用户ID为1的情况有所不同。在第二个参数中,`""` 是一个空字符串,紧接着使用了 `+` 运算符将该空字符串 `""` 和一个Vscript函数 `print("xxx狗都不玩")` 与另一个空字符串 `""` 进行了拼接。由于 `print()` 函数执行后返回 `null` ,而在Squirrel语言中,字符串可以与 `null` 拼接,因此整条语句是可以通过编译的。那么根据Squirrel语言的规则,**在整条语句编译后执行时,** `print("xxx狗都不玩")`   **这一函数将在服务器地图上被执行**,该函数作用是在服务器控制台打印 `xxx狗都不玩` 这一字符串。

​      至此,我们可以看到,**在用户ID为2的玩发送了经过精心构造的特殊的聊天框消息后,服务器执行了一个由用户ID为2的玩家插入在聊天消息中的Vscript函数:**`print()`。如果将 `print()` 函数改成其它Vscript脚本函数,就可以完全控制地图的脚本执行。这是一个非常典型的注入漏洞。

​      当然,不排除有玩家无意中发送了包含双引号字符的聊天框消息的可能,这很可能将导致这一次编译该code时出错,表现为服务器控制台输出报错信息,但不影响游戏正常运行。

​      除了上述 `OnGameEvent_player_say_raw()` 函数外,以下函数同样存在着类似的漏洞:

```squirrel
// 与之前的漏洞利用方法类似,不过这里不是在聊天框输入消息,而是修改自己的Steam用户名类似于"+print("xxx狗都不玩")+"
// 之后再连接到服务器,监听器监听到player_connect事件后,以下函数被执行,被插入在用户名中的函数print()被编译执行
// 函数中接收到的参数event_data.name的值为连接到服务器的玩家的Steam用户名
::OnGameEvent_player_connect_raw <- function(event_data)
{
      local code = " OnPlayerConnect(\"" + event_data.name + "\"," + event_data.userid + ",\"" + event_data.networkid + "\"); ";
      EntFire("event_manager", "RunScriptCode", code, 0.00, null);
      code = " CheckConnected(\"" + event_data.name + "\"," + event_data.userid + ",\"" + event_data.networkid + "\"); ";
      EntFire("event_manager", "RunScriptCode", code, 0.00, null);
}

// 该函数在玩家更改名字时执行
// 例如将名字改为 print("xxx狗都不玩")
::OnGameEvent_player_changename_raw <- function(event_data)
{
      local code = " OnPlayerChangeName(" + event_data.userid + "," + event_data.oldname + "," + event_data.newname+"); ";
      EntFire("event_manager", "RunScriptCode", code, 0.00, null);
}

// 该函数在玩家与服务器断开连接时执行
::OnGameEvent_player_disconnect_raw <- function(event_data)
{
      local code = " OnPlayerDisconnect("+event_data.userid + ",\"" + event_data.reason + "\",\"" + event_data.name + "\",\"" + event_data.networkid + "\"); ";
      EntFire("event_manager", "RunScriptCode", code, 0.00,null);
}
```

​      用一句话总结漏洞原理,即**地图作者编写了一份不严谨的地图的Vscript,在其中使用了一些函数的功能将一个字符串进行了编译并执行,而该字符串包含或拼接了一些可以由玩家控制内容的字符串,且脚本中没有对这些字符串进行严格地检查、过滤或转换,导致注入漏洞产生**。

#### 4. 哪些地图存在漏洞?

​      目前,发现以下地图均存在此漏洞:

>      ze_pizzatime_v9      (披萨时间)
>
>      ze_eternal_grove_v3      (永恒之森)
>
>      ze_best_korea_v1      (最美朝鲜)
>
>      ze_touhou_gensokyo_o4      (幻想乡:阿尔法突袭)
>
>      ze_crazycart_v4      (卤粉卡丁车)
>
>      ze_drakelord_castle_b3      (龙爵的移动城堡)
>
>      ze_japans_3rd_bombing_v8
>
>      ze_noob_too_easy_v3_3a
>
>      ze_ronald_v2

​      漏洞位置详情请看正文之前的**漏洞存在情况概要**部分。

​      有很大可能还存在其它有类似漏洞的地图,只是笔者没有发现。贴主已经联系了Luffaren,协助其修补自己的地图的漏洞。也十分建议各位地图作者与服务器管理员对已有的地图进行漏洞排查和修复。

#### 5. 如何检查地图是否存在漏洞?

​      逐一检查地图中.nut脚本文件和.vmf文件,对使用到 `"RunScriptCode"` 输入的语句和 `compilestring()` 函数的语句,**检查其参数中是否含有或拼接了可由玩家控制内容的组成部分**,若有且未对其进行过滤或转换,则存在注入漏洞。

​      使用Notepad++的文件查找功能和以下正则表达式进行搜索可能更高效,但可能会有遗漏。

> "\s*\+\s*\w{1,20}\s*\+\s*"

​      此正则表达式匹配了脚本中字符串与标识符拼接的语句,如以下语句:

> par = " CheckConnect(" + p.userid + ",\\""+p.name+"\")";
>
> `" + p.userid + "` 和 `"+p.name+"` 将被匹配。

​      检查此语句是否作为 `"RunScriptCode"` 或者 `compilestring()` 的参数,若有则检查该语句中标识符 `p.userid` 和 `p.name` 中是否含有或拼接了可由玩家控制内容的组成部分,若有且未对其进行过滤或转换,则存在注入漏洞。

​      或者着重查看地图中监听以下四种事件的监听器实体的逻辑:

> player_say
>
> player_connect
>
> player_changename
>
> player_disconncet

​      此种方法也可能会有遗漏,因为有可能**产生漏洞的函数**和**监听器实体的执行函数**并没有直接的联系。

#### 6. 修复方法

​      参考Luffaren的修复方法,同时使用stripper插件和Vscript文件。

​      主要思路为,通过stripper文件添加 `logic_relay` 实体,在每回合开始时自动触发与执行新增的Vscript文件中的修复函数,在修复函数中对原有存在漏洞的函数进行覆写,**用安全的方式将参数传入目标函数,而非拼接字符串后编译执行**。

​      以下是Luffaren提供的GFL社区的修复补丁的GitHub链接。

> (https://github.com/gflclan-cs-go-ze/ZE-Configs/blob/master/stripper/ze_pizzatime_v9.cfg)
>
> (https://github.com/gflclan-cs-go-ze/ZE-Configs/blob/master/vscripts/inject_fix.nut)

​      目前,该补丁适用于修复除了 `ze_crazycart_v4` 地图以外的8张地图的漏洞。

------

​      以下是贴主整理的详细的修复方法,其中包括修复 `ze_crazycart_v4` 的方法。

​      **步骤①**

​      在地图stripper插件目录中添加**对应地图名**的stripper文件。

​      stripper插件目录:csgo\addons\stripper\maps\

​      文件名:`要修复的地图名.cfg` (如 `ze_pizzatime_v9.cfg` )

​      并在stripper文件中添加以下语句:

```
; 将文件命名为地图名.cfg
add:
{
      "classname" "logic_relay"
      "targetname" "fix_inject"
      "vscripts" "fix_inject.nut"
      
      "OnSpawn" "!self,Trigger,,0.00,-1"
      "OnSpawn" "!self,Trigger,,0.05,-1"
      "OnSpawn" "!self,Trigger,,0.20,-1"
      "OnTrigger" "!self,RunScriptCode,FixInject(),0.00,-1"
}
```

​      **步骤②**

​      在Vscript脚本目录添加新的Vscript文件。

​      Vscript脚本目录:csgo\scripts\vscripts\

​      文件名:`fix_inject.nut`

​      若自定义其它脚本文件名,请注意脚本文件名和stripper文件中的属性 `"vscripts"` 对应的值保持一致,脚本中函数名和stripper文件里的新增IO语句中 `"RunSciptCode"` 的参数保持一致。

​      **此份脚本不完整。**请根据实际漏洞存在情况对脚本文件内容进行增删改。

```squirrel
// 此函数根据当前地图名寻找对应的管理脚本的实体
// 注意不同地图中的管理脚本的实体的名称有所不同
// 如披萨时间中该实体的名称为"event_manager",永恒之森中的名称为"mapmanager",卤粉卡丁车中的名称为"manager"

// 跳过搜寻管理脚本的实体、让函数直接返回null也是可行的,能修复注入漏洞,但是地图中对应事件的监听功能逻辑也会失效
// (例如在地图幻想乡中,发送"mps"等消息不再播放音频)
GetMapScriptManager <- function()
{
    local name = GetMapName();// 获取当前地图名
    local entScriptManager = null;
   
    if(name.find("ze_pizzatime") == 0)
    {
      entScriptManager = Entities.FindByName(null, "event_manager");      
    }
    else if(name.find("ze_eternal_grove") == 0)
    {
      entScriptManager = Entities.FindByName(null, "mapmanager");      
    }
    else if(name.find("ze_crazykart") == 0)
    {
      entScriptManager = Entities.FindByName(null, "manager");      
    }/*
    else if(name.find("XXXX") == 0)
    {
      entScriptManager = Entities.FindByName(null, "xxxxx");      
    }*/
    else
    {
      //printl("*** fix_inject.nut: 错误!未知的地图。 ***");
      entScriptManager = null;
    }
    return entScriptManager;
}


// 这是每回合开始时要执行的修复函数
FixInject <- function()
{
    // 首先获取地图中管理脚本的实体。
    local entScriptManager = GetMapScriptManager();
   
    // 对存在漏洞的函数进行覆写
    ::OnGameEvent_player_say_raw <- function(event_data) : (entScriptManager)
    {
      if(null != entScriptManager && entScriptManager.IsValid() && entScriptManager.ValidateScriptScope())
      {
            // 使用安全的函数执行方式代替原有以下语句
            // code = "OnPlayerChat("+event_data.userid+",\""+event_data.text+"\")";
            // EntFire("event_manager", "RunScriptCode", code, 0.00, null);
            entScriptManager.GetScriptScope().OnPlayerChat(event_data.userid, event_data.text);
      }
    }


    // 对其他有漏洞的函数进行覆写... ...
    /*
    ::OnGameEvent_player_connect_raw <- function(event_data) : (entScriptManager)
    {
      ... ...
    }
    */


    // 对 ze_crazykart_v4 的函数 M_chat 进行修复
    if(GetMapName().find("ze_crazykart") == 0)
    {
      if(null != entScriptManager && entScriptManager.IsValid() && entScriptManager.ValidateScriptScope())
      {
            // 对存在漏洞的函数M_chat进行覆写
            entScriptManager.GetScriptScope().M_chat <- function()
            {
                while(M_CHAT.len()>0)
                {
                  local event_data = M_CHAT;
                  M_CHAT.remove(0);
                  PlayerChat(event_data.userid, event_data.text);
                }
            }
      }
      else
      {
            EntFire("manager", "RunScriptCode", "M_chat <- function(){while(M_CHAT.len()>0){local c=M_CHAT;M_CHAT.remove(0);PlayerChat(c.userid,c.text);}}");
      }
    }
}
```

------

如有错漏请斧正。

~~能不能给我订张格里斯?~~












云霄雨季 发表于 2023-6-22 02:13:07

{:6_447:}

夏末未央 发表于 2023-6-22 02:20:14

我要玩披萨,前年欠的披萨还没带呢

plogh 发表于 2023-6-22 02:53:42

害怕

五河士郎 发表于 2023-6-22 02:53:48

我去,根林大老虎

Yuera 发表于 2023-6-22 02:54:35

处理中。

smiling 发表于 2023-6-22 09:25:47

不明觉厉{:7_508:}

xK4y1 发表于 2023-6-22 12:39:49

我去,虎子

喝开水的乌龙茶 发表于 2023-6-22 14:41:15

害怕

殇影 发表于 2023-6-22 15:19:37

{:5_481:}

Guaneaer 发表于 2023-6-25 18:44:40

{:4_95:}

不科学 发表于 2023-6-25 21:24:52

所以提供固定的输入让用户选择省时省力 (doge)
页: [1]
查看完整版本: 地图Vscript注入漏洞汇总(2023.06.24)