CTF-Pwn丨栈溢出入门题目思路解析

admin 2023年1月9日12:44:32CTF专场评论4 views5273字阅读17分34秒阅读模式

CTF-Pwn丨栈溢出入门题目思路解析

CTF-Pwn


Pwn作为CTF比赛中的必考题型,用一句话概括就是难以入门


Pwn确实难,难点主要在于知识点不系统、考点太高深、对新手不友好。


其实只要我们
多做多练,了解出题套路,掌握答题思路,还是能够快速入门的。


i 春秋论坛作家「SkYe231」表哥总结了几种Pwn入门栈溢出题目的解题思路,旨在为大家提供更多的学习方法与技能技巧,文章仅供学习参考。


栈溢出入门题目可能不局限下面总结的,诸如利用栈溢出修改局部变量触发某些条件getshell等没有总结到位,感兴趣的小伙伴可以在i春秋官网进行系统学习。

CTF-Pwn丨栈溢出入门题目思路解析

CTF-Pwn丨栈溢出入门题目思路解析


PS:Web端看课体验更佳,看课地址:

https://www.ichunqiu.com/newRelease/webaqgcs


CTF-Pwn丨栈溢出入门题目思路解析


ip根据题目位数不同分别代指eip或rip ,bp及sp同理。以下小结均是基于程序为动态链接。



完整后门&溢出长度大于等于ip


非常基础入门题目,用于了解学习栈溢出控制寄存器ip ,关键代码如下:

int vul()
{
  char s; // [esp+Ch] [ebp-6Ch]
  puts("input:");
  gets(&s); //栈溢出漏洞,溢出长度不限
  return puts("OK,Bye!");
}

通过分析找到后门函数,且这个后门是一个完整可用的后门,就是只要我们调用这个后门函数,不需要其他额外的操作就能getshell,通常是system('/bin/sh'),write(1,'flag',32)等形式。

int getshell()
{
  return system("/bin/sh");
}

思路:


有完整可用后门的情况下,只要溢出长度能够覆盖ip即可。直接利用栈溢出将getshell函数地址写入到eip位置,结束当前函数后就会去运行getshell函数payload结构:

payload = [填充] + [get_shell_addr]



残缺后门&溢出长度远大于ip


栈溢出漏洞函数如下(和上一个小结一样):

int vul()
{
  char s; // [esp+Ch] [ebp-6Ch]
  puts("input:");
  gets(&s); //栈溢出漏洞,溢出长度不限
  return puts("OK,Bye!");
}

这里指的远大于ip,意思是可以溢出覆盖除了ip以外的3个以上的(一般情况,实际情况实际分析)机器字长(32位4 bit;64位8bit)。


残缺后门的形式多种多样:提供诸如system等关键函数;flag、/bin/sh等字符串;某些gadget等。这里就整理前两种情况,刚好对应上面的完整后门。


提供关键函数&字符串


一般给出的是system函数,在ida中一般呈现形式:

int getshell()
{
  return system("echo no no no!!!");
}

这样就能通过system.plt调用system函数。


字符串/bin/sh可以在ida中字符串界面可以查询到(shift+f12),存放在bss区,在源码中通过定义变量来提供。通过地址来调用这个字符串参数。


思路:


对比完整后门发现这里是将函数和参数分割,但是两者都在程序中,所以可以通过栈溢出自行构造完整后门,这里就涉及传参方式方面知识:32位栈传参;64位前6个参数寄存器传参,后续更多参数遵循栈传参。


自行构造完整后门(调用链)对溢出长度有要求了,至少要能溢出除ip以外再多2个机器字长,用于写入调用步骤。


32位程序:

payload=[填充]+[system@plt]+[4bit填充]+[参数]

64位程序:


64位系统寄存器传参需要用到gadget,可以使用ROPgadget查找程序乃至libc库(需要泄露基地址)。一般来说前两个参数rdi rsi的gadget比较常见容易得到的。

payload=[填充]+[pop_rdi_ret]+[参数]+[system@plt]


提供关键函数


关键函数不单单指的system、puts、read、write等我们用于写入读取的辅助函数一定程度上也算是关键函数,但是为了方便总结这里的关键函数指的是system这类函数。


system函数在ida中呈现方式同上小结。与上面小结相比缺少的是字符串/bin/sh,对比完整后门来说就只剩下system。


这里结合上面小结对比缺少一个/bin/sh,那就先构造一个输入的利用链将字符串输入到内存,然后再和上面一样构造system利用链,这里就涉及ROP利用思想。system利用链不再重复,与上面小结一致。输入利用链构根据题目出现输入函数不同而实际分析,一般用的都是read,也有scanf、gets 。


根据溢出长度不同还能细分为两种情况:一次性利用链;ROP利用链。


一次性利用链


这种情况适用于溢出长度比较大,一般能有7个机器字长左右,具体情况具体分析。这种利用的思想是用输入函数read将/bin/sh写入到bss段后调用system('/bin/sh') 。


32位程序:

payload=[填充]+[read@plt]+[[email protected]]+[参数1]+[binsh写入地址]+[参数3]+[binsh写入地址]

64位程序很少用,因为寄存器传参需要用到gadget,这样会导致需要非常大的溢出空间,构造起来和32位思想一样不再重复。


ROP利用链


和一次性利用链思想上最大区别就是运行完自构写入函数后,返回main(或其他)函数,相当于让程序重新运行一次,有第二次利用漏洞机会,写入getshell利用链。


相比之下优势:payload长度变短,有效应对溢出长度不足情况。


32位程序:

payload1=[填充]+[read@plt]+[main]+[参数1]+[binsh写入地址]+[参数3]
payload2=[填充]+[system@plt]+[4bit填充]+[binsh写入地址]

64位程序:


pop_rdi都会有单独的gadget,rsi多数以pop_rsi_r15形式出现,rdx少有gadget,这里假设gadget情况如下,实际做题实际调整:


  • pop_rdi_ret

  • pop_rsi_r15_ret

  • pop_rdx_ret

payload1=[填充]+[pop_rdi]+[参数1]+[pop_rdi_r15]+[binsh写入地址]+[8bit填充]+[pop_rdx]+[参数3]+[read@plt]
payload2=[填充]+[pop_rdi]+[binsh写入地址]+[system@plt]

可以看到payload1还是长的,且rdx gadget不一定有。这是可以尝试不传第三个参数,用寄存器rdx的原值,这个值有可能符合需要(若需要控制前三个参数了解一下ret2csu),payload简化如下:

payload1=[填充]+[pop_rdi]+[参数1]+[pop_rdi_r15]+[binsh写入地址]+[8bit填充]+[read@plt]
payload2=[填充]+[pop_rdi]+[binsh写入地址]+[system@plt]

提供字符串



就是给/bin/sh,其实这样等于没给,需要用到的函数都需要去libc中找,等同于没有后门,故这里不小结,思路和下面无后门一样。



无后门&溢出长度远大于ip


从这种题目开始就接近实际题目了。首先是无后门,也就是既没有system也没有/bin/sh等条件,只有一个栈溢出的漏洞。这个小结中溢出长度定义是远大于ip,就继续沿用上面的漏洞函数gets,实际中更多的是用read函数输入。

int vul()
{
  char s; // [esp+Ch] [ebp-6Ch]
  puts("input:");
  gets(&s); //栈溢出漏洞,溢出长度不限
  return puts("OK,Bye!");
}

思路:


既然程序中没有system就到libc中找,也就是需要泄露libc基地址。方法是泄露一个位于libc中的函数的真实地址,将真实地址减去函数偏移得到基地址。/bin/sh也能在libc中找到不再赘述。


泄露地址需要一个(程序中出现过的)输入函数,常见的有puts、write等。泄露完成后需要第二次利用漏洞的机会,所以也就是涉及ROP利用思想。


32位程序:


puts

payload1=[填充]+[puts@plt]+[main]+[某函数@got]
payload2=[填充]+[system@libc_addr]+[4bit填充]+[[email protected]_addr]

write

payload1=[填充]+[puts@plt]+[main]+[某函数@got]
payload2=[填充]+[system@libc_addr]+[4bit填充]+[[email protected]_addr]

64位程序:


puts

payload1=[填充]+[pop_rdi]+[某函数@got]+[[email protected]]+[main]
payload2=[填充]+[pop_rdi]+[binsh@libc_addr]+[[email protected]_addr]

write


稳妥来说需要控制3个寄存器:write(1,[输出地址],[输出长度]) 。如果找不到rdx的gadget和[上面小结](#ROP利用链)一样尝试看看rdx原值是否符合需求,如果要不满足就得用ret2csu技巧控制前三个寄存器。



无后门&溢出长度等于ip


溢出只能够刚好覆盖完ip就不能再溢出更多字节,显然是不能写入完整的利用链,这种情况可以考虑栈迁移。栈迁移就是将原本写在ip后面的利用链写到其他地方,通过控制bp、ip将栈迁移到利用链的位置,运行提前布置的利用链。


首先这种题目会有一到两次输入的机会,输入内容可能会是局部变量或者全部变量(bss段)。栈迁移需要明确的迁移地址,基于存放位置不同难度也不同:


全局变量


写到bss段就比较简单了,迁移地址直接到ida查全局变量地址即可。


局部变量


局部变量在栈上,栈地址是动态的,所以要先泄露栈地址才能进行栈迁移,但是也有用sub esp,xx这种神奇gadget将栈抬高的操作。如果用迁移到栈上一般会有两次输入一次,一次是泄露栈地址,一次是写入利用链。


需要注意一点:假如写入地址和迁移地址为addr,有效利用链需要从addr+一个机器周期开始写入,具体自行调试查看sp变化。


思路


为了便于总结假设一个32位程序有两次输入,第一次向全局变量var_1(地址为:addr)写入,第二次向局部变量s写入。

// 32位程序
int vul()
{
  char s; // [esp+Ch] [ebp-6Ch]
  puts("input:");
  gets(&var_1); //栈溢出漏洞,溢出长度不限
  puts("input:");
  read(0,&s,0x74);  //栈溢出漏洞,溢出长度有限
  return puts("OK,Bye!");
}

提前用ROPgadget找到leave;ret gadget,然后构造payload如下:

payload1='a'*4+p32(puts@plt)+p32(main)+p32([email protected])
payload2='a'*0x6c+p32(addr)+p32(leave;ret)

泄露地址后返回main函数,第二次运行程序。后续getshell可以用onegadget或者system(/bin/sh)。溢出ip为onegadget就能getshell。system(/bin/sh)需要再次栈迁移。


onegadget

payload3=[任意内容]
payload4='a'*0x6c+'a'*4+p32(onegadget)

system(/bin/sh)

payload3='a'*4+p32(system@libc_addr)+p32(main)+p32([email protected]_addr)
payload4='a'*0x6c+p32(addr)+p32(leave;ret)

对于32位程序如果输出函数只有write影响不大,而64位程序如果输出函数只有write没有puts等少参数的函数,可能会比较棘手,原因还是在于第三个参数rdx缺少gadget,需要实际调试查看rdx的原值是否需求,如果不符合需要赋值就要ret2csu,将csu利用链写到迁移地址上。



总结


这里归纳了几种入门题目常见套路,但是套路远远不局限于上文,比如栈溢出修改局部变量,绕过PIE保护等等,后续会在公众号分享相关内容,大家敬请关注。


文章素材来源于i春秋社区


注:文章内容仅供学习交流使用,任何人不得将其用于非法用途,否则后果自行承担!


相关推荐






猎聘SRC入驻i春秋:千元京东卡邀你解锁


【车联网安全】汽车CAN总线探索之旅


白帽专访丨月神:我的The loner安全团队












CTF-Pwn丨栈溢出入门题目思路解析


CTF-Pwn丨栈溢出入门题目思路解析

END



CTF-Pwn丨栈溢出入门题目思路解析

i春秋官方公众号为大家提供

前沿的网络安全技术

简单易懂的实用工具

紧张刺激的安全竞赛

还有网络安全大讲堂

更多技能等你来解锁

CTF-Pwn丨栈溢出入门题目思路解析


CTF-Pwn丨栈溢出入门题目思路解析

点分享

CTF-Pwn丨栈溢出入门题目思路解析

点点赞

CTF-Pwn丨栈溢出入门题目思路解析

点在看

原文始发于微信公众号(i春秋):CTF-Pwn丨栈溢出入门题目思路解析

特别标注: 本站(CN-SEC.COM)所有文章仅供技术研究,若将其信息做其他用途,由用户承担全部法律及连带责任,本站不承担任何法律及连带责任,请遵守中华人民共和国安全法.
  • 我的微信
  • 微信扫一扫
  • weinxin
  • 我的微信公众号
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年1月9日12:44:32
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                  CTF-Pwn丨栈溢出入门题目思路解析 http://cn-sec.com/archives/602344.html

发表评论

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: