D3_car不完全解题思路

也可移步Hor1zon公众号

https://mp.weixin.qq.com/s/aXr3E1THtLmYbAFnYnuCPA

D3_car_flag1

题目环境太垃圾了,安卓环境一直断😅
其实很简单,一开始有点懵,想加个监看进去看看,日过车的都知道scrcpy是个好工具😋
https://github.com/Genymobile/scrcpy

可是这个环境真的太卡了,上了scrcpy之后那是一点就断;背景这几个字暗示了要找到车的GPS数据,但是这个导航怎么点都点不开,还老是断服务,后边发现根本没装车载导航(地图)服务;
所以还是接着命令行操作,先来看一下应用列表 哎 您猜怎么着?
一眼就看到了一个非常可疑的APP
package:com.d3car.factory

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
generic_x86_64:/ $ pm list packages
package:com.android.cts.priv.ctsshim
package:com.android.car.messenger
package:com.android.providers.telephony
package:com.google.android.car.kitchensink
package:com.android.providers.calendar
package:com.android.providers.media
package:com.android.wallpapercropper
package:com.android.car.media
package:com.android.car.radio
package:com.android.car.trust
package:com.android.documentsui
package:com.android.externalstorage
package:com.android.htmlviewer
package:com.android.companiondevicemanager
package:com.android.quicksearchbox
package:com.android.mms.service
package:com.android.providers.downloads
package:com.android.car.overview
package:android.car.cluster.loggingrenderer
package:com.android.defcontainer
package:com.android.car.mapsplaceholder
package:com.android.providers.downloads.ui
package:com.android.pacprocessor
package:com.android.certinstaller
package:com.android.carrierconfig
package:android
package:com.android.contacts
package:com.android.camera2
package:com.android.car.systemupdater
package:com.android.car
package:com.android.egg
package:com.android.mtp
package:com.android.nfc
package:com.android.backupconfirm
package:com.android.provision
package:com.android.statementservice
package:com.android.calendar
package:com.android.systemui.theme.dark
package:com.android.car.hvac
package:com.android.providers.settings
package:com.android.sharedstoragebackup
package:com.android.printspooler
package:com.android.dreams.basic
package:com.android.car.dialer
package:com.android.webview
package:com.android.inputdevices
package:com.android.support.car.lenspicker
package:com.android.bips
package:com.android.musicfx
package:com.android.cellbroadcastreceiver
package:android.ext.shared
package:com.android.onetimeinitializer
package:com.android.server.telecom
package:com.android.keychain
package:com.android.printservice.recommendation
package:com.android.gallery3d
package:android.ext.services
package:com.android.calllogbackup
package:com.android.packageinstaller
package:com.android.carrierdefaultapp
package:com.svox.pico
package:com.android.car.media.localmediaplayer
package:com.android.proxyhandler
package:com.android.inputmethod.latin
package:org.chromium.webview_shell
package:com.d3car.factory
package:android.car.usb.handler
package:com.android.managedprovisioning
package:com.android.dreams.phototable
package:com.android.smspush
package:android.car.cluster.sample
package:com.android.wallpaper.livepicker
package:com.android.storagemanager
package:jp.co.omronsoft.openwnn
package:com.android.bookmarkprovider
package:com.android.settings
package:com.android.calculator2
package:com.android.cts.ctsshim
package:com.android.vpndialogs
package:com.android.email
package:com.android.music
package:com.android.phone
package:com.android.shell
package:com.android.wallpaperbackup
package:com.android.providers.blockednumber
package:com.android.providers.userdictionary
package:com.android.emergency
package:com.android.location.fused
package:com.android.deskclock
package:com.android.systemui
package:com.android.bluetoothmidiservice
package:com.android.bluetooth
package:com.android.providers.contacts
package:com.android.captiveportallogin
generic_x86_64:/ $
generic_x86_64:/ $
generic_x86_64:/ $
generic_x86_64:/ $ pm path com.d3car.factory
package:/system/priv-app/D3Factory/D3Factory.apk
generic_x86_64:/ $

PS G:\> adb pull /system/priv-app/D3Factory/D3Factory.apk
/system/priv-app/D3Factory/D3Factory.apk: 1 file pulled, 0 skipped. 0.1 MB/s (38128 bytes in 0.354s)

简单逆向就可以找到flag了)

1
2
3
4
5
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:gravity="center" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent">
<TextView android:textSize="@dimen/common_textsize_24" android:id="@+id/flag1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="20dp" android:text="d3ctf{yes_you_f1nd_b4ckdo0r}"/>
<Button android:id="@+id/tcpdump" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="open/close tcpdump"/>
</LinearLayout>

也可以用另一种方法,直接拉起应用的Activity

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
generic_x86_64:/ $ dumpsys package com.d3car.factory | grep -i activity
Activity Resolver Table:
4d5331e com.d3car.factory/.FactoryActivity filter 1beb95a
b8e17ff com.d3car.factory/.PwdAuthActivity filter ee66505
b8e17ff com.d3car.factory/.PwdAuthActivity filter ee66505
android.permission.USER_ACTIVITY: granted=true
android.permission.MANAGE_ACTIVITY_STACKS: granted=true
android.permission.USER_ACTIVITY: granted=true
android.permission.MANAGE_ACTIVITY_STACKS: granted=true
generic_x86_64:/ $
generic_x86_64:/ $
generic_x86_64:/ $
generic_x86_64:/ $ am start -n com.d3car.factory/.FactoryActivity
Starting: Intent { cmp=com.d3car.factory/.FactoryActivity }
generic_x86_64:/ $
generic_x86_64:/ $
generic_x86_64:/ $

D3_car_flag2

1
2
PS G:\> adb pull /system/priv-app/   #我甚至把所有的app都拖了下来)
PS G:\> adb pull /system/priv-app/D3Factory/oat/x86_64


这里一开始是先去把odex转成dex
利用了baksmali-2.5.2.jar和smali-2.5.2.jar,转成的dex发现只还原了方法名,里面的核心的代码根本看不到,再试试把vdex转成dex,windows环境及其不友好,最后在ubuntu虚拟机完成了还原
所需要的工具为:https://github.com/anestisb/vdexExtractor
🤗项目下载下来后直接./make.sh gcc编译就行,然后会在bin目录下生成vdexExtractor文件😚
执行命令:./vdexExtractor -i xxx.vdex -o ./
最后jadx打开该dex,即可看到核心代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
package com.d3car.factory;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

public class PwdAuthActivity extends Activity {
private Context mContext;
private Button mEnterBtn;
private TextView mKeyText;
private EditText mPwdText;

@Override // android.app.Activity
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(2130837504);
this.mPwdText = (EditText) findViewById(2131165185);
this.mEnterBtn = (Button) findViewById(2131165186);
this.mKeyText = (TextView) findViewById(2131165184);
this.mContext = getApplicationContext();
Log.d("D3CTF", "onCreate: ");
this.mEnterBtn.setOnClickListener(new View.OnClickListener() { // from class: com.d3car.factory.PwdAuthActivity.1
@Override // android.view.View.OnClickListener
public void onClick(View v) {
PwdAuthActivity.this.verifyPWD(PwdAuthActivity.this.mPwdText.getText().toString(), PwdAuthActivity.this.mKeyText.getText().toString());
}
});
}

public void verifyPWD(String pwd, String key) {
if (pwd.length() == 0) {
Toast.makeText(this, getString(2131099707), 0).show();
} else if (verifyPassWord(pwd, key)) {
Log.d("D3CTF", "verifyPWD: the password is right!");
startActivity(new Intent(this, FactoryActivity.class));
} else {
Toast.makeText(this, getString(2131099706), 0).show();
}
}

public static boolean verifyPassWord(String password, String key) {
if (key == null || key.isEmpty()) {
return "20240419".equals(password);
}
return "uVQPRRYTrpvqvCv6".equals(encryptPwdByKey(password, key)); //加密后结果进行比较
}

public static String encryptPwdByKey(String pwd, String key) {
char[] pwd_arr = pwd.toCharArray();
char[] key_arr = key.toCharArray();
char[] result = new char[pwd.length()];
for (int i = 0; i < pwd.length(); i++) {
result[i] = (char) (pwd_arr[i] ^ key_arr[i % pwd.length()]);
}
return new String(result);
}
}


验证代码的核心在这里,其实逆向逻辑还是很简单,就是一个异或,但是这里的数据真是诱导了我,看这里的逻辑是传入了key和密码。因为即使要逆向,这两个数据你得有一个才行,一开始我以为这个20240419是key,然后逆向出密码,发现不对,这就不得不开始看一下代码的整体逻辑

这个TextView?再来看看登录页面


key就是屏幕上的字符串😂,然后直接梭脚本就行

1
2
3
4
5
6
7
8
9
data="uVQPRRYTrpvqvCv6"
key="D2036445ADCGF12W7"
pwd=""
for i in range(len(data)):
pwd+=chr(ord(data[i])^ord(key[i]))
print(pwd)

#1dacdfma34560rDa
#在我输入密码验证的时候环境多次掉线!但是最后发现这个和题目没什么关系 面子工程.jpg

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package com.d3car.factory;

import android.app.Activity;
import android.os.Bundle;
import android.os.SystemProperties;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import java.io.IOException;

private CheckBox mTcpDump;

@Override // android.app.Activity
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(2130837506);
this.mTcpDump = (CheckBox) findViewById(2131165189);
if (SystemProperties.getInt("sys.tcpdump", 0) == 1) {
this.mTcpDump.setChecked(true);
} else {
this.mTcpDump.setChecked(false);
}
this.mTcpDump.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { // from class: com.d3car.factory.TcpdumpActivity.1
@Override // android.widget.CompoundButton.OnCheckedChangeListener
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
SystemProperties.set("sys.tcpdump", "1");
try {
Runtime.getRuntime().exec("/system/xbin/tcpdump -i any -s 0 -w /sdcard/tcpdumplog.pcap -W 1");
} catch (IOException e) {
e.printStackTrace();
}
} else {
SystemProperties.set("sys.tcpdump", "0");
try {
Runtime.getRuntime().exec("pkill tcpdump");
} catch (IOException e2) {
e2.printStackTrace();
}
}
}
});
}
}

在adb直接使用tcpdump显示被ban了,可以看到程序后门是一个tcpdump服务

用自带的APP后门去抓包,可以看到存在mqtt服务再循环往复的发送同样的包。

抓到MQTT服务的主题&账号&密码(断的太快了 没机会传扫描工具到IVI里面扫😭扫一下应该就能扫到其他ECU了

1
2
3
4
5
6
7
Client ID Length: 11
Client ID: Gojo_Satoru
User Name Length: 11
User Name: abmaM_kcalb
Password Length: 14
Password: Ya5_1_n4C_tahW

以及两条MQTT的报文,这里的mqtt是在做can的转发,在环境中实现uds的功能

1
2
3
4
5
Topic: can/514/write
Message: 022701ffffffffff #看到2701 血脉觉醒 是UDS服务 这里给了did为514的ecu的pincode

Topic: can/514/write
Message: 0527022dcf28ffff


环境中应该不存在模拟的CAN总线(向出题人求证了这一点),ivi这里应该是通过mqtt与其他ECU通信,所以可以先用22服务去读其他ecu信息,然后要过掉27服务,进入QNX拿flag,也向出题人求证了这一点,flag果然在QNX里面。这里难点在于确定qnx的did,然后就通过mqtt执行can/ecu id/write来执行诊断命令;还有就是要把环境代理到本地来发mqtt广播包;

现在没有环境也没法再具体分析操作,看了官方和W&M师傅的wp,感觉总体思路上和我的想法差不多,可以用fscan扫到一些seed2key.dll,也就是27服务密钥算法,逆向得到key,而且qnx还存在31服务,后边的过程参考W&M师傅的wp,写的很细🫡
https://blog.wm-team.cn/index.php/archives/75/#D3+car+2

D3_car_flag3

无地图服务的背景提示联想到flag应该和GPS有关

前面查看应用包发现有一个com.android.location.fused的APP,这里我的思路有点偏了)我跑过去研究这个APP,考虑从这个app中拿到gps定位,但我再也没连上过靶机😭也无法验证是否可行。
和前面一样,把odex转成dex,可以看到车机是由GPS服务的,只是需要一个APP去调用GPS权限即可拿到定位信息,下一步想法就是搞一个地图/定位APP adb install上去来获取经纬度信息。

从车机安全性考虑,一般都会采用私有证书签名,在/system/etc/security找到了车机的证书信息,通过伪造签名证书就可以让APP安装到车机上面了。

看了W&M师傅的wp真的给跪了,通过相机调用GPS信息再来拿到经纬度信息的想法真的太绝了Orzzzzzz
我倒是也打开过车机设置APP但他真的太卡了🥲基本上点两下就会断线,我就得再次启动安卓服务,然后他再次断线😭

由于环境无了,这里引用凌武出题人师傅的截图,装好定位APP就可以拿到经纬度了。

因为目前国内的车载地图服务商除了特斯拉基本都是高德,所以直接去高德搜索经纬度

然后再保利·江语海东南门这里看到出题人的用户评价,进个人主页就可以拿到flag了。


D3_car不完全解题思路
https://g1at.github.io/2024/04/30/d3car/
作者
g0at
发布于
2024年4月30日
许可协议