本文原创作者:W1ngs,本文属i春秋原创奖励计划,未经许可禁止转载!
前言:最近在入门pwn的栈溢出,做了一下jarvisoj里的一些ctf pwn题,感觉质量都很不错,难度循序渐进,把自己做题的思路和心得记录了一下,希望能给入门pwn的朋友带来点帮助和启发,大牛轻喷

题目链接:https://www.jarvisoj.com/challenges
1、level0(64位)
代码
<ignore_js_op><ignore_js_op> 
也就是输出Hello World以后接收一个输出,仔细看会发现一个system函数
<ignore_js_op> 
那么思路就很清晰,在read函数接收输入的时候直接覆盖返回地址为system函数即可

在IDA中可以看到,buf距离EBP为0x80,但是这个是64位的程序,一个EBP占8bytes,那么payload:

[AppleScript] 纯文本查看 复制代码
1
payload = 'a' *0x80 + 'a'*8(ebp的地址)+p32(system函数的地址)
  • p32也就是pack32,把地址的hex值转化为32小端地址存储的方法进行发送

脚本如下:
<ignore_js_op> 
运行脚本得到shell
<ignore_js_op>

2、level1

0x00 代码
    main函数
<ignore_js_op> 
   vulnerable_function函数
<ignore_js_op> 
可以看到buf的偏移是0x88,那么需要的填充:'a'*0x88+'a'*4(EBP)+返回地址
0x01 第一种解法

使用pwntools自带的工具checksec查看程序的保护机制,发现是NX disabled,可以直接往栈上写汇编语句来执行来达到溢出的目的

基本思路:这里printf给出了buf的首地址,所以可以填充返回地址为接收到的buf的首地址,用asm模块向上面写数据,这个也是pwntools自带的模块
使用方法:

[AppleScript] 纯文本查看 复制代码
1
asm(shellcraft.sh())

运行以后可以看到,打印出来的地址的值就是buf的首地址,我们用pwntools接收
<ignore_js_op>

  • 这里接收到的buf_addr一定要进行int16进制的转换( int(buf,16) ),才能用p32封包进行发送

最后的脚本:
<ignore_js_op> 
E:/%E6%9C%89%E9%81%93/qq987FE2B65EC068C281254890044944CD/f76f4bc855a34842a1163a96af15d769/clipboard.png

<ignore_js_op>

0x02 第二种解法
    这种方法算是一个通法,也就是使用dynelf的方法来动态的泄露system函数的地址,这里先记住这个方法,后面还会说到

dynelf使用的条件:栈溢出、有输入输出函数(gets、puts、read、write)、无PIE保护

这里在第一输入点进行栈溢出,leak出system函数的地址并返回主函数
<ignore_js_op> 
这里的leak函数计算出libc的基地址,并查找libc库来泄露system函数的地址
调用read函数向bss可写段写入/bin/sh字符串,接着调用system拿到shell
<ignore_js_op>

  • 这里使用gdb的vmmap的方法找到可写段,使用后面的数据段不容易出错

<ignore_js_op>

3、level2
main函数
<ignore_js_op> 
   vulnerable_function函数
<ignore_js_op> 
存在栈溢出,而且可以直接调用system函数,搜索字符串还可以发现有/bin/sh字符串
<ignore_js_op> 
思路:通过溢出到返回地址修改为system函数的地址,参数为/bin/sh字符串
此时栈的分布情况:
<ignore_js_op>

最后的脚本:
<ignore_js_op> 
payload = 'a' *0x88 + 'a'*4 + p32(system_addr) + p32(1) + p32(bin_addr)

  • p32(1)为无效的填充的返回地址,这里我们调用完system函数就行了,不用管这个地址
  • 这里使用elf的search方法来搜索字符串,next方法取到第一个匹配到的字符串

这里的ELF方法可以打印输出基本的plt、got表中函数的地址,就不需要我们手动进行查找了,具体的可以看下面的文章
https://blog.csdn.net/weixin_41400278/article/details/78819950

这里也可以通过read函数来向可写段写入/bin/sh字符串,思路就是先调用read函数写入字符,之后返回主函数,再向栈内写入system函数,这是在没有/bin/sh字符串的时候使用。在这里就显得多此一举了
<ignore_js_op>

4、level3
代码:
<ignore_js_op> 
<ignore_js_op>

这里有read、write函数,但是没有system函数,这题还给了libc库,所以这题就是使用比较标准的got表泄露system函数的方法来getshell
<ignore_js_op> 
思路:使用write函数将got表中write地址作为参数,调用plt表中的write函数,得到基地址后加上system函数在程序中的偏移,就得到了system函数的实际地址,进行调用即可
(1)、使用ELF函数获取plt、got表的地址
<ignore_js_op> 
(2)、构造payload
<ignore_js_op>

因为这里的plt_write的地址是将jmp语句传入栈中,后面还传入了三个参数,所以为了平衡栈需要pop三次(使用ROPgadget查找,rop方法的一种)
https://blog.csdn.net/weixin_40850881/article/details/80216764

在qira中看到的情况是这样的
<ignore_js_op> 
(3)、减去write函数在libc库中的偏移得到基地址
<ignore_js_op>

write函数的偏移地址的查找:
先使用ldd命令找到libc库的名字
<ignore_js_op>

然后用readelf方法找到函数的相对偏移位置
<ignore_js_op> 
(4)、构造system函数并调用
payload1: payload = "A" * 0x88 + "A" * 4 + p32(plt_write) + p32(main_addr) + p32(1) + p32(elf.got["write"]) + p32(4)

也就是先调用write(1,write函数地址,4),第一个参数文件描述符等于1表示输出,4代表输出的长度,中间的就是输出的内容,调用完成后返回主函数,得到基地址后为泄露system函数的地址做准备

先leak出write函数的libc地址,从而计算出libc的基地址,sendline之后返回主函数
<ignore_js_op>

<ignore_js_op> 
leak出system函数的地址
<ignore_js_op>

运行脚本的结果:
<ignore_js_op> 
在input输入后调用system函数
payload2 = "A" * 0x88 + "A" * 4 + p32(system_addr) + p32(1) + p32(binsh_addr)

找到字符串/bin/sh在libc库中的位置(注意这里是偏移地址),可以在IDA中直接找到,也可以用上面说的search方法
<ignore_js_op> 
<ignore_js_op>

根据libc的基地址,得到system函数的地址,/bin/sh字符串的地址,sendline出payload以后就会执行system函数
<ignore_js_op>

  • 这里最后用sleep函数暂停一下

运行脚本getshell
<ignore_js_op>

0x03 level4
代码
<ignore_js_op> 
<ignore_js_op>

思路:这题和上面的level3几乎一样,但是这道题没有给出libc库,所以这里使用的是pwntools工具包里的DynELF,利用DynELF工具来泄漏system函数的地址,再往bss段写入"/bin/sh"字符串,接着调用system函数
具体可以看下这里:
https://blog.csdn.net/guiguzi5512407/article/details/52752909

dynelf : 用于远程符号泄漏,需要提供leak方法,简单来说就是将原来需要输出的got表的地址处替换成了需要寻找的system函数的地址
<ignore_js_op> 
按照前面的思路,正常的写法是先leak出write函数的地址
<ignore_js_op> 
得到的write地址:0xf76da900
<ignore_js_op>

使用leak方法泄露出system函数的地址,脚本会从github上下载相应的libc库
<ignore_js_op> 
system函数的地址:0xf75a2e70

<ignore_js_op> 
调用read函数向bss段填充/bin/sh字符串,这里为了不影响程序的正常执行
<ignore_js_op> 
   这里为了平衡栈(不影响程序的正常执行)需要pop三个参数,再传入read的三个参数:0,/bin/sh字符串写入的地址,8(/bin/sh的长度,末尾为\x00
   接着直接调用system函数,传入的参数为/bin/sh字符串的地址,发送payload之后,sendline内容为"/bin/sh"的字符串

运行脚本拿到shell
<ignore_js_op>

0x04 结束语
    所有的脚本下载:https://github.com/H4lo/jarvisoj_pwn
  这几道题虽然算是ROP的入门题,但是正常的解法也就是这些方法,再掌握一些进阶的ROP技巧,比如绕过canary保护之类的,再加上多刷点题,那么CTF比赛中的栈溢出几乎就没有问题了。
  总的来说就是多刷题,在刷题中学习思路和技巧,但是最基本的pwn知识要掌握,比如栈的结构,基本的程序运行机制和流程,最后再推荐几个学习和刷题的网站:
http://www.whaledu.com
http://pwnable.kr

大家有任何问题可以提问,更多文章可到i春秋论坛阅读哟~

pwn入门之栈溢出练习的更多相关文章

  1. CTF必备技能丨Linux Pwn入门教程——栈溢出基础

    这是一套Linux Pwn入门教程系列,作者依据i春秋Pwn入门课程中的技术分类,并结合近几年赛事中出现的一些题目和文章整理出一份相对完整的Linux Pwn教程. 课程回顾>>Linux ...

  2. Linux pwn入门教程(10)——针对函数重定位流程的几种攻击

    作者:Tangerine@SAINTSEC 本系列的最后一篇 感谢各位看客的支持 感谢原作者的付出一直以来都有读者向笔者咨询教程系列问题,奈何该系列并非笔者所写[笔者仅为代发]且笔者功底薄弱,故无法解 ...

  3. Linux pwn入门教程——CTF比赛

    Linux pwn入门教程(1)——栈溢出基础 from:https://zhuanlan.zhihu.com/p/38985585   0x00 函数的进入与返回 要想理解栈溢出,首先必须理解在汇编 ...

  4. CTF丨Linux Pwn入门教程:针对函数重定位流程的相关测试(下)

    Linux Pwn入门教程系列分享已接近尾声,本套课程是作者依据i春秋Pwn入门课程中的技术分类,并结合近几年赛事中出现的题目和文章整理出一份相对完整的Linux Pwn教程. 教程仅针对i386/a ...

  5. CTF必备技能丨Linux Pwn入门教程——stack canary与绕过的思路

    Linux Pwn入门教程系列分享如约而至,本套课程是作者依据i春秋Pwn入门课程中的技术分类,并结合近几年赛事中出现的题目和文章整理出一份相对完整的Linux Pwn教程. 教程仅针对i386/am ...

  6. CTF必备技能丨Linux Pwn入门教程——PIE与bypass思路

    Linux Pwn入门教程系列分享如约而至,本套课程是作者依据i春秋Pwn入门课程中的技术分类,并结合近几年赛事中出现的题目和文章整理出一份相对完整的Linux Pwn教程. 教程仅针对i386/am ...

  7. CTF必备技能丨Linux Pwn入门教程——格式化字符串漏洞

    Linux Pwn入门教程系列分享如约而至,本套课程是作者依据i春秋Pwn入门课程中的技术分类,并结合近几年赛事中出现的题目和文章整理出一份相对完整的Linux Pwn教程. 教程仅针对i386/am ...

  8. CTF必备技能丨Linux Pwn入门教程——利用漏洞获取libc

    Linux Pwn入门教程系列分享如约而至,本套课程是作者依据i春秋Pwn入门课程中的技术分类,并结合近几年赛事中出现的题目和文章整理出一份相对完整的Linux Pwn教程. 教程仅针对i386/am ...

  9. CTF必备技能丨Linux Pwn入门教程——调整栈帧的技巧

    Linux Pwn入门教程系列分享如约而至,本套课程是作者依据i春秋Pwn入门课程中的技术分类,并结合近几年赛事中出现的题目和文章整理出一份相对完整的Linux Pwn教程. 教程仅针对i386/am ...

随机推荐

  1. blender基础操作

    旋转:鼠标中键 左右移动:鼠标中键+左shift 放大缩小:鼠标滚轮滚动 blender旋转以锁定物件作为中心点旋转 blender选取物件用鼠标右键, 选中物件之后,利用数字键盘中的点(Del) 来 ...

  2. 传递参数:java代码中形参的改变有没有影响实参?

    实参:可以是常量.变量.表达式.函数等, 无论实参是何种类型的量,在进行函数调用时,它们都必须具有确定的值, 以便把这些值传送给形参. 因此应预先用赋值,输入等办法使实参获得确定值. 形参:全称为“形 ...

  3. Android自动化之Monkey测试(二)

    本文主要从以下方面进行分享. 一.查看应用包名二.Monkey启动三.Monkey停止四.Monkey命令五.日志分析 一.查看应用包名 大多数时候,我们都是对特定的应用进行monkey测试,因此需要 ...

  4. python 打包成 windows .EXE

    1. 升级pip python -m pip install --upgrade pip 2.安装 pyinstall (打包程序) pip install pyinstaller 3 开始打包(打包 ...

  5. pypi batch download

    https://wiki.archlinux.org/index.php/Python_package_guidelines_(%E7%AE%80%E4%BD%93%E4%B8%AD%E6%96%87 ...

  6. mysql中的concat函数,concat_ws函数,concat_group函数之间的区别

    一.CONCAT()函数CONCAT()函数用于将多个字符串连接成一个字符串.使用数据表Info作为示例,其中SELECT id,name FROM info LIMIT 1;的返回结果为+----+ ...

  7. py3.0第四天 函数

    列表生成 # -*- coding: utf-8 -*- # data =[1,2,3] # for index,i in enumerate(data): # print (index,i) # d ...

  8. JPA-学习02

    一.主键生成策略 主键:确定一张表的唯一性东西(非空且唯一) 分为:自然主键和代理主键. 生成策略: identity:自增策略(1.值必须是数字,2.数据库支持) sequence:序列策略(同上, ...

  9. 虚拟机运行docker后映射端口到windows开发机

    1.运行容器(以Oracle-xe-11g为例) 运行容器时先在虚拟机内映射端口 docker run -d -p 9090:8080 -p 1521:1521 wnameless/oracle-xe ...

  10. 贪心-Wooden Sticks

    先将火柴按照长度(或重量)优先排序,在不断遍历数组,找出其中重量(长度)递增子序列,并标记 Problem Description There is a pile of n wooden sticks ...