【精选】使用GPT还原Redis CVE-2023-28425漏洞细节的全过程!

admin 2023年4月4日04:17:27评论189 views字数 7547阅读25分9秒阅读模式

【精选】使用GPT还原Redis CVE-2023-28425漏洞细节的全过程!

扫码进群

介绍

本篇文章展示如何使用GPT剖析CVE漏洞未公布详情的情况中分析 Redis源代码,为 CVE-2023-28425 编写一个简单的 PoC。此外,还提供了一些有关如何阅读开源项目的源代码以查找已知错误漏洞的技巧。最后,我将向您展示我如何定期使用 chatGPT 来查找有关目标的更多信息。  


开始

Redis 是一种开源的内存数据结构存储,广泛用作数据库、缓存和消息代理。它于 2009 年首次发布,此后凭借其高性能、可扩展性和灵活性成为最受欢迎的 NoSQL 数据库之一。总体而言,Redis 是一种功能强大且用途广泛的数据存储,已在广泛的应用程序和行业中得到广泛使用,从社交媒体和电子商务到金融和医疗保健。  

CVE-2023-28425

如图中所说,受影响版本中当 MSETNX 命令中两次使用相同的键将触发断言,通过 Redis 身份认证的攻击者可利用此漏洞造成拒绝服务。  

【精选】使用GPT还原Redis CVE-2023-28425漏洞细节的全过程!

当然,产品所有者不想公开有关漏洞的更多信息。我们得到的消息只有cve 公告与一些简单描述。假设公告描述是对的,我们从内容中提取下列信息:  

  • 受影响的版本>= 7.0.8

  • 应用补丁7.0.10

  • 该漏洞很容易触发,只有经过身份验证的用户才能触发它

  • 触发漏洞后,它将被运行时断言捕获,导致 redis 服务器退出 (DoS)

  • 发现漏洞的作者

信息收集  

第一步应该叫做信息收集,顾名思义就是收集知识的行为。当然你可以直接进入代码库,但我个人更喜欢在这部分花更多的时间。步骤细分为:  

  1. 在 twitter/dorks/github/gitlab 上搜索 CVE

  2. 在 github/gitlab 问题和提交部分寻找关键字,在这种情况下“MSETNX”可能是一个不错的选择

  3. 关注漏洞报告者在社交媒体中的内容

在此阶段,我发现:redis 内部注释、CVE-2022-31144 POC。  

代码检查  

步骤细分为:  

  1. 使用 git diff 将已打补丁的版本与版本号与已打补丁版本最接近的未打补丁版本进行比较。

  2. Grep,按关键字搜索

  3. 大型项目使用 eclipse,否则 vim+cscope 更好

  4. 从chatGPT中得到指令

让我们做第一个子步骤:  

git diff 7.0.9 7.0.10  

我们尝试寻找关键字“MSETNX”,在检查差异后,我们发现为“MSETNX with not existing keys - same key twice”添加了一个测试用例,也许这就是漏洞。  

【精选】使用GPT还原Redis CVE-2023-28425漏洞细节的全过程!

我们问 chatGPT 关于 MSETNX 命令漏洞。为了得到好的结果,我们需要告诉 chatgpt 成为某一个角色。我是这样做的:在提出漏洞相关的问题之前,我会在前面加上以下句子:   

"As a <role> <adjective-role>, your are my assistant."<role>  

例如“安全代码审核员,安全研究员,漏洞研究员”和<adjective>“熟练,专家”等。您可以组合角色。  

现在问chatGPT ,不能简单使用“如何触发 CVE-2023-…”。因为首先他数据库内容为2021年(就像我们以为的那样!),其次这个问题可能被标记为敏感问题并且答案将不会那么完整。

我推荐大家使用英文,中文会误解意思。 

(例如给我讲一个关于男人 vs. 给我讲个女人的笑话)。  

我这样逃避它:"I am analyzing <project-name> project which is hosted on github at <url>, and i have found the following vulnerability: "<vulnerability-description>". Can you explain it better and maybe give an example ?"。  

我正在分析托管在Github上的<project-name>项目,网址为<url>,我发现了以下漏洞:“<vulnerability-description>”。您能否更好地解释一下并且给出一个例子?  

使用的最后一个问题:  

As security code auditor expert and skilled bug bounty hunter, your are myassistant. I am analyzing redis project which is hosted on github herehttps://github.com/redis/redis, and i have found the following vulnerability"Authenticated users can use the MSETNX command to trigger a runtime assertionand termination of the Redis server process.", can you explain better and maybegive an example ?  

作为安全代码审计专家和熟练的漏洞赏金猎人,您是我的助手。我正在分析Redis项目,这个项目托管在GitHub上,链接是https://github.com/redis/redis,我发现了以下漏洞:“认证用户可以使用MSETNX命令触发运行时终止Redis服务器进程。”,您能否更好地解释一下,并且给出一个例子?  

【精选】使用GPT还原Redis CVE-2023-28425漏洞细节的全过程!

搭建测试环境  

# ubuntu 20.04 docker imagegit clone https://github.com/redis/rediscd redisgit checkout 7.0.9export CFLAGS="-g"makecd src# run redis server#gdb -ex "run" --args ./redis-server --port 7777./redis-server --port 7777# run client./redis-cli -h 127.0.0.1 -p 7777  

试试chatgpt给出的poc。  

【精选】使用GPT还原Redis CVE-2023-28425漏洞细节的全过程!

让我们试试在补丁版本上添加的测试用例。很酷,我们找到了漏洞。  

【精选】使用GPT还原Redis CVE-2023-28425漏洞细节的全过程!

漏洞分类(?)  

使用“-g”符号标志编译源代码,然后在触发程序崩溃后通过up <#frame>anddown <#frame>命令检查 gdb 上的函数调用堆栈帧。在我们的例子中,我们这样做up 4,然后发出context命令来更新 gdb 控制台视图。  

【精选】使用GPT还原Redis CVE-2023-28425漏洞细节的全过程!

【精选】使用GPT还原Redis CVE-2023-28425漏洞细节的全过程!

我们找到了运行时断言发生的位置。让我们检查源代码。在我的例子中,我使用 cscope 如下:  

# append this to bashrc or only for the current bash session# Cscope configexport 

CSCOPE_EDITOR=`which vim` alias cscope_cpp="find . -iname '*.cpp' -o -iname '*.c' -o -iname '*.h' -o -iname '*.hpp' -o -iname '*.cc' > cscope.files" 

alias cscope_java="find . -iname '*.java' > cscope.files" 

alias cscope_py="find . -iname '*.py' > cscope.files" 

alias cscope_all="find . -iname '*.cpp' -o -iname '*.c' -o -iname '*.h' -o -iname '*.hpp' -o -iname '*.cc' -o -iname '*.java' -o -iname '*.py' > cscope.files" 

alias cscope_database="cscope -q -R -b -i cscope.files" 

alias cscope_Clean='rm cscope.in.out cscope.po.out cscope.files cscope.out'cd redis

cscope_cpp

cscope_database

cscope -d  

我们搜索“查找此全局定义”部分,使用“TAB”从/到查询部分移动。  

【精选】使用GPT还原Redis CVE-2023-28425漏洞细节的全过程!

所以回到dbAdd功能。我们得出断言发生的结论,因为它de != NULL。  

/* Add the key to the DB. It's up to the caller to increment the reference * counter of the value if needed. * * The program is aborted if the key already exists. */void dbAdd(redisDb *db, robj *key, robj *val) {    sds copy = sdsdup(key->ptr);    dictEntry *de = dictAddRaw(db->dict, copy, NULL);    serverAssertWithInfo(NULL, key, de != NULL);    dictSetVal(db->dict, de, val);    signalKeyAsReady(db, key, val->type);    if (server.cluster_enabled) slotToKeyAddEntry(de, db);    notifyKeyspaceEvent(NOTIFY_NEW,"new",key,db->id);}      

让我们检查dictAddRaw功能。使用 gdb 我们发现这

if ((index = _dictKeyIndex(d, key, dictHashKey(d,key), existing)) == -1)

是真的,这意味着键已经存在于字典中,因此返回 NULL,这将触发前面解释的断言条件。  

/* Low level add or find: * This function adds the entry but instead of setting a value returns the * dictEntry structure to the user, that will make sure to fill the value * field as they wish. * * This function is also directly exposed to the user API to be called * mainly in order to store non-pointers inside the hash value, example: * * entry = dictAddRaw(dict,mykey,NULL); * if (entry != NULL) dictSetSignedIntegerVal(entry,1000); * * Return values: * * If key already exists NULL is returned, and "*existing" is populated * with the existing entry if existing is not NULL. * * If key was added, the hash entry is returned to be manipulated by the caller. */dictEntry *dictAddRaw(dict *d, void *key, dictEntry **existing){    long index;    dictEntry *entry;    int htidx;if (dictIsRehashing(d)) _dictRehashStep(d);/* Get the index of the new element, or -1 if     * the element already exists. */if ((index = _dictKeyIndex(d, key, dictHashKey(d,key), existing)) == -1)        return NULL;/* Allocate the memory and store the new entry.     * Insert the element in top, with the assumption that in a database     * system it is more likely that recently added entries are accessed     * more frequently. */htidx = dictIsRehashing(d) ? 1 : 0;    size_t metasize = dictMetadataSize(d);    entry = zmalloc(sizeof(*entry) + metasize);    if (metasize > 0) {        memset(dictMetadata(entry), 0, metasize);    }    entry->next = d->ht_table[htidx][index];    d->ht_table[htidx][index] = entry;    d->ht_used[htidx]++;/* Set the hash entry fields. */dictSetKey(d, entry, key);    return entry;}      

结尾

最后,该漏洞是由于在MSETNX执行的同一命令上两次声明新密钥引起的。特别是漏洞存在于msetGenericCommand解析MSETNX k1 val1 k1 val2命令时调用的函数中。因为nx不等于 0,并且因为命令声明的键都不存在于数据库中,所以setkey_flags |= SETKEY_DOESNT_EXIST;发生了。然后我们循环声明的每个键并使用相同的setkey_flagssetKey(c, c->db, c->argv[j], c->argv[j + 1], setkey_flags);。这对于第一个声明是正确的k1 val1,但对于k1 val2已经k1声明并且不应该SETKEY_DOESNT_EXIST设置位的是错误的setkey_flags。  

void msetGenericCommand(client *c, int nx) {    int j;    int setkey_flags = 0;if ((c->argc % 2) == 0) {        addReplyErrorArity(c);        return;    }/* Handle the NX flag. The MSETNX semantic is to return zero and don't     * set anything if at least one key already exists. */if (nx) {                                                               // [0]for (j = 1; j < c->argc; j += 2) {            if (lookupKeyWrite(c->db,c->argv[j]) != NULL) {                addReply(c, shared.czero);                return;            }        }        setkey_flags |= SETKEY_DOESNT_EXIST;                                // [1]}for (j = 1; j < c->argc; j += 2) {        c->argv[j+1] = tryObjectEncoding(c->argv[j+1]);        setKey(c, c->db, c->argv[j], c->argv[j + 1], setkey_flags);         // [2]notifyKeyspaceEvent(NOTIFY_STRING,"set",c->argv[j],c->db->id);    }    server.dirty += (c->argc-1)/2;    addReply(c, nx ? shared.cone : shared.ok);}

 

差异补丁:  

diff --git a/src/t_string.c b/src/t_string.cindex af58d7d54..4659e1861 100644--- a/src/t_string.c+++ b/src/t_string.c@@ -561,7 +561,6 @@ void mgetCommand(client *c) { void msetGenericCommand(client *c, int nx) {     int j;-    int setkey_flags = 0;     if ((c->argc % 2) == 0) {         addReplyErrorArity(c);@@ -577,12 +576,11 @@ void msetGenericCommand(client *c, int nx) {                 return;             }         }-        setkey_flags |= SETKEY_DOESNT_EXIST;     }     for (j = 1; j < c->argc; j += 2) {         c->argv[j+1] = tryObjectEncoding(c->argv[j+1]);-        setKey(c, c->db, c->argv[j], c->argv[j + 1], setkey_flags);+        setKey(c, c->db, c->argv[j], c->argv[j + 1], 0);         notifyKeyspaceEvent(NOTIFY_STRING,"set",c->argv[j],c->db->id);     }     server.dirty += (c->argc-1)/2;      

POC地址

关注公众号后回复:CVE-2023-28425



总结

本文介绍了使用chatGPT分析一个CVE漏洞的办法,该漏洞存在于Redis项目中,允许认证用户使用MSETNX命令触发运行时断言和Redis服务器进程的终止。文章提供了漏洞分类、测试环境的搭建、漏洞的原因和补丁等详细信息。同时,文章还提供了POC和差异补丁。  

往期回顾

01

一次完整的GPT-4代码审计,挖掘CMSeasy漏洞

02

从零开始,手把手教您如何开通ChatGPT Plus开启GPT-4之旅(depay,nobepay,某宝)

03

我使用ChatGPT审计代码发现了200多个安全漏洞(GPT-4与GPT-3对比报告)

04

ChatGPT 乐于编写勒索软件,只是真的很不擅长

05

想免费白嫖GPT-4,不花钱?你要的来了!!



作为一名网络安全行业从业者和公众号运营者,我深感网络安全运营的困难。在我的职业生涯中,我不断努力学习和提升自己,不断探索和尝试新的创作方式和思路,以更好地满足读者和粉丝的需求。

在这个过程中,我始终坚信,主体全力的拼搏,离不开大家的支持和关注。正是您的支持和鼓励,让我能够在自媒体领域不断前行、不断成长。

因此,我诚挚地邀请您关注我的公众号,让我们一起分享、一起学习、一起进步。在未来的创作中,我将不断努力,为大家带来更好的内容,为自媒体行业的发展贡献自己的一份力量。

感谢您的支持和关注!



作为一名网络安全行业从业者和公众号运营者,我深感网络安全运营的困难。在我的职业生涯中,我不断努力学习和提升自己,不断探索和尝试新的创作方式和思路,以更好地满足读者和粉丝的需求。

在这个过程中,我始终坚信,主体全力的拼搏,离不开大家的支持和关注。正是您的支持和鼓励,让我能够在自媒体领域不断前行、不断成长。

因此,我诚挚地邀请您关注我的公众号,让我们一起分享、一起学习、一起进步。在未来的创作中,我将不断努力,为大家带来更好的内容,为自媒体行业的发展贡献自己的一份力量。

感谢您的支持和关注!


【精选】使用GPT还原Redis CVE-2023-28425漏洞细节的全过程!

加作者微信,邀请进群。







原文始发于微信公众号(安全女巫):【精选】使用GPT还原Redis CVE-2023-28425漏洞细节的全过程!

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年4月4日04:17:27
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   【精选】使用GPT还原Redis CVE-2023-28425漏洞细节的全过程!https://cn-sec.com/archives/1649248.html

发表评论

匿名网友 填写信息