【mugen】猴子也能看懂的mugen凶恶原理(1)——%n

工作之后惊讶的发现mugen中的凶恶技术居然和自己的工作相关,这下不得不研究一下了,错误之处评论区可以纠正。
1. 什么是mugen
mugen是一个PC平台的格斗游戏引擎,许多国家的人为其制作或者移植了非常多的格斗人物,变成了一款及其罕见的超多人制作的大乱斗游戏。
而又因为其程序本身存在漏洞(越界,栈溢出,字符串格式化),这些漏洞被利用起来加强人物杀伤力,导致出现了更罕见的凶恶玩法。
2. %n初步学习
mugen中最出名也是最简单的一个中期凶恶技术,叫做%n,本质上是一个字符串格式化漏洞。
以kfm和winmugen为例,mugen人物的逻辑代码(比如定义一个人物的一个动作,执行这个动作会调用哪些图片,哪个音效,加多少气等等)位于kfm.cns中。
需要先定义一个Statedef(状态号),再在下面写State(状态控制器),感受下如下代码。
凶恶代码可以写在[Statedef -3]中,因为它是最优先运行的代码,而且每一帧都会运行一次。比如可以写一个简单的加气的代码。
能稍微看懂这些我们就可以来尝试%n了,代码示例如下。
很明显,存在漏洞的控制器是DisplayToClipBoard,以及AppendToClipBoard,从控制器名称可以看得出来,这个功能本来是提供给制作者一个可以在剪切板中自由获取值的接口。
它可以给Text设置占位符,同时输入params,就会写4931656(0x4b4048)内存上的值为0x30。任意调试器(od)打开mugen,载入人物进入watch就会发现地址成功被改写。

好吧,这么看来都不需要字符串格式化漏洞那些高端的玩法,直接就是一个任意地址写。
我们来尝试追踪下漏洞代码,由于已经知道了会写0x4b4048,可以下内存写入的断点,可以发现能断在0x496CB6|mov dword ptr ds:[eax],ecx

Crtl+F9步进到retn之后回到0x4713AE,进入程序自定义的方法sub_46E800()。

可以发现,上面的call 004967DF就是call _vsprintf,那么漏洞代码发生在这里。a2和ArgList均可控制,这在pwn中是一个非常明显的字符串格式化漏洞,通过%n可以实现任意地址写。

重新断点在0x4713A9,发现call之前已经完成压栈。

就这样简简单单我们拥有了任意地址写。
3. %n初步利用
有了任意地址写,那么如何利用呢?先实验一个简单的修改当前人物信息的思路。
找到4B5B4C,这个位置存储着mugen主程序绝对路径的指针。


可以发现下方存储着其他mugen相关属性,参考https://qxmugen.com/article/11933.html
这里为了方便区分,弄两个kfm人物包,并且修改name和/displayname
基地址3650048
+CD4,select.def内加载的人物数量
03650D1C 00000002
+F7C,选人界面里,共有多少行
03650FC4 00000007
+B654,P1 name指针
+B658,P2 name指针
0365B69C 0CFC1C78 ASCII "kfm"
0365B6A0 0D7AEAA0 ASCII "kfm2"

0CFC1C78 006D666B kfm.
P1 name指针+30 displayname
0CFC1CA8 676E754B Kung
0CFC1CAC 20754620 Fu
0CFC1CB0 006E614D Man.

如果手动用调试器修改0CFF1CA8地址的值,即可实时修改游戏中的显示的人物名

如果我们拿%n修改人物名或者其他相关属性不就可以达到目的了吗?
但是由于ALSR原因,基地址3650048是不确定的,我们需要从4B5B4C拿到指针才知道具体的,而%n是无法返回某地址的值的(pwn中的传统字符串格式化漏洞是可以的)。所以我们只能改写那些固定的值,其中就包括按键输出。
https://qxmugen.com/article/13197.html
mugen中F1可以让2P侧快速死亡,因此改写这4B5948/4B594C/4B5548三个地址的值就够了,可以多加一个判断1P/2P的代码,从而决定是否加ctrl(会导致1P死亡)。
这样,一个最简单的%n利用F1攻击的代码就出现了。