作者 | 安全女巫
责编 | 王子彧
前面使用 GPT-4 对部分代码进行漏洞审计,后面使用 GPT-3 对 git 存储库进行对比。最终结果仅供大家在 chatgpt 在对各类代码分析能力参考,其中存在误报问题,不排除因本人训练模型存在问题导致,欢迎大家对误报结果进行留言,我会第一时间跟进处理~
大家若想看更全面的内容,请先关注我并发送私信,我会根据大家反馈写一篇 GPT-4 对某代码更全面的分析报告。
前言
GPT-3 在此 git 存储库中发现了 213 个安全漏洞。而相比之下,市场上很好的商业工具却只发现了 99 个安全漏洞。尽管商业工具以更结构化的格式提供上下文,在手动审查 GPT-3 检测到的 99 / 213 个漏洞的随机样本后,有 4 个是误报。代码审计大家都清楚,存在误报是常规操作。
其中各类脚本漏洞占比结果如下:
分析漏洞数量总体占比:
简介
近年来,ChatGPT 已成为互联网新宠,长期以来一直对现代人工智能的潜在影响持怀疑态度的网络安全行业似乎也注意到了这一点,因为他们担心 ChatGPT 可能会被资源有限、技术知识为零的黑客滥用。
目前人工智能和机器学习领域取得了巨大的发展,如此便开辟了一个新的可能性领域-基于 AI 的代码分析,如何使用 AI 模型来检测代码中的安全漏洞尤为惹人关注。
文中使用 OpenAI 的 GPT-4对部分代码进行分析,文末针对GPT-3 如何在包含 129 个易受攻击文件的代码存储库中查找安全漏洞。如果大家感兴趣,我会单独为大家更新哦!
如何运行
GPT-4 使用 Plus 会员版本,跟官网同配置,可处理长代码。( PS:若有朋友需要开通 PLus 会员,私信留言,将提供保姆级教程和邀请码,能优惠购买哦~)
GPT-3 变体(text-davinci-003)具有 4000 个标记的上下文窗口,大约为 3000 个英文单词。这意味着它最多可以处理几百行代码的请求,无法一次性处理整个代码库。
为了解决这个问题,只能使用 GPT-3 扫描单文件。这意味着 GPT-3 难以找到由多个代码文件交互引起的安全漏洞,除非进行足够多的提示词引导。特别是当源代码使用常见的库,如 express.js、Flask、Python 标准库、C 标准库等时。可能 GPT-3 有相关记录。在本文分析的代码中,GPT-3对导入库有相关记录就能够准确地检测到安全漏洞,而无需检查任何导入库的代码。
公平地说,我怀疑许多现有的商业漏洞扫描工具在进行静态分析时实际上并不检查导入库的代码,因此这与市场上某些工具的工作方式并没有太大的不同。
被分析的代码存储库中的每个文件夹都以一种安全漏洞命名,并包含带有示例代码的文件,包含一个或多个漏洞。其中一些文件包含琐碎的代码,但许多是您可能会在生产代码库中遇到同样的代码片段(注意:尽管它们仍然是片段,因此缺乏更大代码库的上下文)。存储库中每个文件夹的 README.md 包含 GPT-3 对该文件夹中所有文件的安全漏洞的分析。
使用的漏洞代码库:
https://github.com/chris-koch-penn/gpt3_security_vulnerability_scanner
开篇我是用 GPT-4 分析,后面代码将会用 GPT-3 的进行对比。
1》代码注入漏洞检测
PHP 代码片段1(tarlogic-ex2.php):
检测结果:
这段代码存在一个漏洞,即通过 PHP 的 unserialize 函数,可以实现远程代码执行。具体原因如下:
为了修复这个漏洞,可以考虑以下几点:
避免使用 unserialize 函数处理不可信的数据。可以使用其他方法(如 JSON)来传输和解析数据。
对于具有潜在危险的魔术方法,如 __destruct,要特别小心。确保在使用这些方法时,不会引入安全隐患。
结果还是很详细。
2》Flask 反序列化漏洞
Python 代码(pickle2.py):
GPT-4对长代码分析能力:
import osimport sysimport subprocessimport commandsimport pickleimport cPickleimport marshalimport os.pathimport filecmpimport globimport linecacheimport shutilimport dircacheimport ioimport timeitimport popen2import codeimport codeopimport ptyimport posixfile
SECRET_KEY = 'you will never guess'
if not os.path.exists('.secret'):with open(".secret", "w") as f:secret = ''.join(random.choice(string.ascii_letters + string.digits)for x in range(4))f.write(secret)with open(".secret", "r") as f:cookie_secret = f.read.strip
app = Flask(__name__)app.config.from_object(__name__)
black_type_list = [eval, execfile, compile, open, file, os.system, os.popen, os.popen2, os.popen3, os.popen4, os.fdopen, os.tmpfile, os.fchmod, os.fchown, os.open, os.openpty, os.read, os.pipe, os.chdir, os.fchdir, os.chroot, os.chmod, os.chown, os.link, os.lchown, os.listdir, os.lstat, os.mkfifo, os.mknod, os.access, os.mkdir, os.makedirs, os.readlink, os.remove, os.removedirs, os.rename, os.renames, os.rmdir, os.tempnam, os.tmpnam, os.unlink, os.walk, os.execl, os.execle, os.execlp, os.execv, os.execve, os.dup, os.dup2, os.execvp, os.execvpe, os.fork, os.forkpty, os.kill, os.spawnl, os.spawnle, os.spawnlp, os.spawnlpe,os.spawnv, os.spawnve, os.spawnvp, os.spawnvpe, pickle.load, pickle.loads, cPickle.load, cPickle.loads, subprocess.call, subprocess.check_call, subprocess.check_output, subprocess.Popen, commands.getstatusoutput, commands.getoutput, commands.getstatus, glob.glob, linecache.getline, shutil.copyfileobj, shutil.copyfile, shutil.copy, shutil.copy2, shutil.move, shutil.make_archive, dircache.listdir, dircache.opendir, io.open, popen2.popen2, popen2.popen3, popen2.popen4, timeit.timeit, timeit.repeat, sys.call_tracing, code.interact, code.compile_command, codeop.compile_command, pty.spawn, posixfile.open, posixfile.fileopen]
@app.before_requestdef count:session['cnt'] = 0
@app.route('/')def home:remembered_str = 'Hello, here\'s what we remember for you. And you can change, delete or extend it.'new_str = 'Hello fellow zombie, have you found a tasty brain and want to remember where? Go right here and enter it:'location = getlocationif location == False:return redirect(url_for("clear"))return render_template('index.html', txt=remembered_str, location=location)
@app.route('/clear')def clear:flash("Reminder cleared!")response = redirect(url_for('home'))response.set_cookie('location', max_age=0)return response
@app.route('/reminder', methods=['POST', 'GET'])def reminder:if request.method == 'POST':location = request.form["reminder"]if location == '':flash("Message cleared, tell us when you have found more brains.")else:flash("We will remember where you find your brains.")location = b64e(pickle.dumps(location))cookie = make_cookie(location, cookie_secret)response = redirect(url_for('home'))response.set_cookie('location', cookie)return responselocation = getlocationif location == False:return redirect(url_for("clear"))return render_template('reminder.html')
class FilterException(Exception):def __init__(self, value):super(FilterException, self).__init__('The callable object {value} is not allowed'.format(value=str(value)))
class TimesException(Exception):def __init__(self):super(TimesException, self).__init__('Call func too many times!')
def _hook_call(func):def wrapper(*args, **kwargs):session['cnt'] += 1print session['cnt']print args[0].stackfor i in args[0].stack:if i in black_type_list:raise FilterException(args[0].stack[-2])if session['cnt'] > 4:raise TimesExceptionreturn func(*args, **kwargs)return wrapper
def loads(strs):reload(pickle)files = StringIO(strs)unpkler = pickle.Unpickler(files)unpkler.dispatch[pickle.REDUCE] = _hook_call(unpkler.dispatch[pickle.REDUCE])return unpkler.load
def getlocation:cookie = request.cookies.get('location')if not cookie:return ''(digest, location) = cookie.split("!")if not safe_str_cmp(calc_digest(location, cookie_secret), digest):flash("Hey! This is not a valid cookie! Leave me alone.")return Falselocation = loads(b64d(location))return location
def make_cookie(location, secret):return "%s!%s" % (calc_digest(location, secret), location)
def calc_digest(location, secret):return sha256("%s%s" % (location, secret)).hexdigest
if __name__ == '__main__':app.run(host="0.0.0.0", port=5051)
检测结果:
这段 Python 代码使用 Flask 框架实现了一个 Web 应用,用户可以设置、获取和删除提醒。但是,代码中存在一些安全问题:
代码使用了 Python 的 pickle 模块来序列化和反序列化用户输入的数据。1.pickle 存在已知的安全风险,尤其是在处理不可信的数据时。攻击者可能通过构造恶意的 pickle 数据来执行任意代码。虽然代码中使用了 _hook_call 函数对 pickle.loads 进行了一定程度的安全控制,但依然不能保证绝对安全。建议使用更安全的替代方案,如 JSON。
2.代码中使用了一个名为 SECRET_KEY 的静态密钥,这可能会导致安全隐患。为了提高安全性,可以考虑使用更强大的密钥生成策略,并定期更换密钥。
3.使用 sha256 进行摘要计算,虽然目前没有被证明不安全,但为了提高安全性,可以考虑使用更安全的哈希算法,如 bcrypt。
4.使用了不安全的函数,如 eval、execfile 和 os.system 等。虽然代码中定义了一个 black_type_list 来限制这些危险函数的使用,但在实际开发中,应尽量避免使用这些具有潜在安全风险的函数。
5.代码在 app.run 中将 host 设置为 "0.0.0.0",这意味着应用将监听所有可用的网络接口。在生产环境中,这可能会导致潜在的安全问题。建议仅在开发环境下使用 "0.0.0.0",生产环境应使用具体的 IP 地址或域名。
综上所述,这段代码存在一定的安全风险。在实际应用中,需要对这些问题进行修复,并关注其他方面的安全性,如 HTTPS 连接、访问控制等。
给出的分析师比 GPT-3.5 全面很多,可以对比 GPT-3.5 的结果。
3》命令注入漏洞检测
PHP 代码2:
分析结果:
这段 PHP 代码存在一定的安全隐患:
综上所述,这段代码存在安全风险。在实际应用中,需要对这些问题进行修复,并关注其他方面的安全性,如 HTTPS 连接、访问控制等。
4》 XML 外部实体注入(XXE)攻击漏洞检测
PHP 代码3:
echo"Sorry, $email is already registered!"; ?>
分析结果:
这段代码存在一个安全问题,即 XML 外部实体(XXE)注入攻击的风险。问题出在使用 libxml_disable_entity_loader(false) 函数,它允许解析外部实体。攻击者可以通过构造恶意的 XML 输入,来读取服务器上的敏感文件或发起网络攻击。
为了修复这个问题,可以通过禁用外部实体加载来防止 XXE 攻击。
将代码中 libxml_disable_entity_loader(false) 改为libxml_disable_entity_loader(true)。
同时,使用 LIBXML_NOENT 和 LIBXML_DTDLOAD 选项时要谨慎,因为它们可能会导致安全问题。
另外,这段代码没有对用户输入进行验证和转义,可能导致其他安全问题,例如跨站脚本攻击(XSS)。因此,在处理用户输入时,请务必进行有效的验证和转义。
5》XML 外部实体注入(XXE)攻击漏洞检测
这代码比较长,大家看代码库中 XmlReader_Tests.cs,GPT-4 能处理长脚本。具有 token 长的特点。我就截图给大家看代码长度。
分析结果:
这个测试代码包含三个测试用例,分别测试了使用 XmlReaderSettings 和 XmlReader 对 XML 文档的解析。我们逐个分析:
对 GPT-4 的审计能力总结:
GPT-4 在代码审计过程中展示了以下几个方面的能力:
在代码审计过程中,展现出了较强的安全意识和分析能力,并通过动态调试和模拟执行更深入地理解代码逻辑。然而,安全审计是一个复杂且持续的过程,需要不断学习和提高。
GPT-3 分析代码结果
实例1:
这是一个容易受到格式化字符串攻击的简单 C 程序:
检测到的漏洞:
1.未经验证的用户输入:程序不检查用户输入的长度,这可能导致缓冲区溢出攻击。
2.格式化字符串漏洞:程序不检查用户输入的格式,可能导致格式化字符串攻击。
实例2:
让我们用高级语言编写的一个不那么简单的程序来试试这个,比如这个包含 Log Forging 漏洞的 C# 程序。我个人在开源项目和生产代码库中看到过类似(尽管不那么琐碎)的代码:
检测到漏洞:
1.日志注入漏洞:代码容易受到日志注入攻击,因为用户输入未经任何清理就被直接记录下来。
实例3:
以下 C 程序读取和操作图像。它包含许多安全漏洞,包括越界读取和写入:
printf("\n\tHeader\twidth\theight\tdata\t\r\n");while(fread(&img,sizeof(img),1,fp)>0){printf("\n\t%s\t%d\t%d\t%s\r\n",img.header,img.width,img.height,img.data);int size1 = img.width + img.height;char* buff1=(char*)malloc(size1);memcpy(buff1,img.data,sizeof(img.data));free(buff1);if (size1/2==0){free(buff1);}else{if(size1 == 123456){buff1[0]='a';}}int size2 = img.width - img.height+100;//printf("Size1:%d",size1);char* buff2=(char*)malloc(size2);memcpy(buff2,img.data,sizeof(img.data));int size3= img.width/img.height;//printf("Size2:%d",size3);char buff3[10];char* buff4 =(char*)malloc(size3);memcpy(buff4,img.data,sizeof(img.data));char OOBR_stack = buff3[size3+100];char OOBR_heap = buff4[100];buff3[size3+100]='c';buff4[100]='c';if(size3>10){buff4=0;}else{free(buff4);}
free(buff2);}fclose(fp);}
int main(int argc,char **argv){ProcessImage(argv[1]);}
检测到漏洞:
1.未检查的返回值:未检查 fopen 函数的返回值,这可能导致空指针取消引用。
2.越界读取:memcpy 函数用于将数据从 img.data 复制到 buff1 和 buff2,而不检查目标缓冲区的大小,这可能导致越界读取。
3.越界写入:buff3 和 buff4 数组的写入没有检查源缓冲区的大小,这可能导致越界写入。
4.未初始化的内存访问:OOBR*stack 和 OOBR*
heap 变量在未初始化的情况下被访问,这可能导致未定义的行为。
5.内存泄漏:如果 size3 变量大于 10,则不会释放 buff4 数组,这可能导致内存泄漏。
GPT-3 对于其中 5 个中的 4 个是正确的。漏洞 1 是误报,因为 fopen 在行中检查了的值 if(fp == NULL)。此外,漏洞 4 的措辞具有误导性——它引用的代码行中存在两个未初始化的内存访问错误是正确的,但它指向了错误的变量名。
实例4:
最后一个 Java 示例是我最喜欢的,因为不幸的是,当我第一次阅读这段代码时,我没有发现它有任何问题。对我来说,它看起来完全无害且熟悉——我在野外看到过类似的代码,但从未有过任何第二个想法。
// Serialization of the object to fileSystem.out.println("Serializing " + obj.toString + " to " + filename);out.writeObject(obj);
out.close;file.close;}catch(Exception e){System.out.println("Exception: " + e.toString);}}
// Function to deserialize an object from a filepublic static Object DeserializeFromFile(String filename){Object obj = new Object;
try{FileInputStream file = new FileInputStream(filename);ObjectInputStream in = new ObjectInputStream(file);
// Deserialization of the object to fileSystem.out.println("Deserializing from " + filename);obj = in.readObject;
in.close;file.close;}catch(Exception e){System.out.println("Exception: " + e.toString);}
return obj;}
检测到的漏洞:
1.不安全的反序列化:反序列化不受信任的数据会导致远程代码执行。
2.不安全的文件权限:正在写入或读取的文件可能具有不安全的权限,允许未经授权的访问。
网络安全的终极“左移”
左移是在开发过程的早期应用安全实践的过程——比如在将代码部署到生产环境之前测试安全漏洞。
通过应用最佳安全实践并在开发人员编写和提交代码时对网络安全进行教育,人工智能代码分析可以代表网络安全的最终左移。这可以真正在组织内部培养强大的网络安全文化,甚至可以防止下一次大黑客攻击,但是……
诚然,这项技术还不完美,它遗漏了几件事,而经验丰富的网络安全专业人员进行审计时不会遗漏的。尽管如此,作为通用的大型语言模型,它还是出奇的好,而且比其他替代方案便宜得多。
GPT-4 目前没有发布日期,但我相信这些大型语言模型将随着获得越来越多的功能而继续向前发展。
一个误报的例子在手动检查 GPT-3 检测到的 213 个漏洞中的大约 60 个之后,我发现其中一个值得注意的误报位于此 Java 程序的第 13 行:
app.get('/', function(req, res) {if (req.cookies.profile) {var str = new Buffer(req.cookies.profile, 'base64').toString;var obj = serialize.unserialize(str);if (obj.username) {res.send("Hello " + escape(obj.username)); // <--- GPT-3 makes a mistake here}} else {res.cookie('profile', "eyJ1c2VybmFtZSI6ImFqaW4iLCJjb3VudHJ5IjoiaW5kaWEiLCJjaXR5IjoiYmFuZ2Fsb3JlIn0=", {maxAge: 900000,httpOnly: true});}res.send("Hello World");});app.listen(3000);
检测到的漏洞:
未验证的用户输入反序列化(cookie)
由于未转义用户输入(用户名)而导致的潜在 XSS 漏洞
GPT-3 对前两个漏洞的看法是正确的,但第三个漏洞是误报——被obj.username逃逸了,但 GPT-3 说它不是。
结果:
实验结果表明,GPT-3 能够检测到扫描的 129 个文件中的 85 个文件中的安全漏洞。能力不亚于一个代码审计专家!
{name:'.php', value:50}, {name:'.js', value:20}, {name:'.cs', value:16}, {name:'.c', value:14}, {name:'.java', value:9}, {name:'.py', value:8}, {name:'.rb', value:5}, {name:'.asp', value:3}, {name:'.ts', value:2}, {name:'.go', value:1}, {name:'.html', value:1}
{name:'发现漏洞数', value:73}, {name:'未发现漏洞', value:43}, {name:'发现漏洞', value:6}, {name:'回答', value:2}, {name:'潜在漏洞', value:2}, {name:'分析', value:1}, {name:'安全漏洞', value:1}, {name:'未提供回答', value:1}
更多 asp,php,java,c,js,cs 等脚本的分析结果请查看文章前面代码库。
总结
本文介绍了使用 ChatGPT 审计代码的过程和结果。作者使用了 GPT-3 检测到了 213 个安全漏洞,其中 85 个文件被正确检测到。GPT-3 的误报率相对较低,但仍有一些漏洞被错报或漏报。作者认为,GPT-3 可以作为一种辅助工具来帮助发现安全漏洞,但不能完全取代人工审计。
GTP-4 能处理长代码,就意味着不用将代码进行切割,不用过多引导。在代码审计过程中,展现出了较强的安全意识和分析能力,并通过动态调试和模拟执行更深入地理解代码逻辑。虽然比 GPT-3 强大许多,但还是有很长的路需要走。
原文链接:https://mp.weixin.qq.com/s/uVFCbaKDYeF9zmVdVXDnUg
本文由 CSDN 获得授权后发布,转载请联系安全女巫获得授权。
☞Google 搜索引擎将加入 AI 聊天功能;澳洲地方市长或就诽谤信息起诉 ChatGPT;Swift 5.8 发布|极客头条
☞ C++ 增幅一骑绝尘,“黑马” Zig 闯入前 50 名!TIOBE 4 月榜单发布
☞ 震动硅谷!43 岁知名技术大佬当街遇刺身亡,马斯克怒批暴力犯罪