Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ELF: Detect OS from Go binaries #1987

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
Open

Conversation

williballenthin
Copy link
Collaborator

@williballenthin williballenthin commented Feb 14, 2024

use the strategies pioneered by GoReSym to detect the target OS for ELF binaries compiled by Go:

  • find the GOOS configuration, and
  • find embedded Go source filenames

closes #1978
FYI @C0d3R3ad3r
FYI @stevemk14ebr

Checklist

  • No CHANGELOG update needed
  • No new tests needed
  • No documentation update needed

@williballenthin williballenthin added the enhancement New feature or request label Feb 14, 2024
Copy link
Collaborator

@mr-tz mr-tz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, my only comment would be on tests around this
if not part of this PR let's create an issue to track adding samples/tests

Comment on lines +1102 to +1103
# content: {ff 20 47 6f 20 62 75 69 6c 64 69 6e 66 3a 04 02}
# content: {ff 20 47 6f 20 62 75 69 6c 64 69 6e 66 3a 08 02}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add a comment what these are?

@williballenthin
Copy link
Collaborator Author

thank you for the feedback - i was being lazy. i'll add some test files and some test cases.

@@ -957,6 +960,397 @@ def guess_os_from_symtab(elf: ELF) -> Optional[OS]:
return None


def is_go_binary(elf: ELF) -> bool:
for shdr in elf.section_headers:
if shdr.get_name(elf) == ".note.go.buildid":
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure how reliable expecting the note to be present will be. Floss does this:

https://github.com/mandiant/flare-floss/blob/b2ca8adfc5edf278861dd6bff67d73da39683b46/floss/language/identify.py#L88

capa/features/extractors/elf.py Show resolved Hide resolved
@stevemk14ebr
Copy link
Contributor

I would recommend testing on old go versions prior to 1.18 when buildinfo was added. I would also recommend testing with binaries emitted by garble and gobfuscate which can mess with symbol names. Otherwise LGTM with the above notes in mind

# the current buildinfo data region.
#
# Brute force the k-v pair, like `GOOS=linux`,
# rather than try to parse the data, which would be fragile.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is totally fine and should always work, but the buildinfo is just a string with some separator control words so could be easy to parse too.

Writing of buildinfo: https://github.com/golang/go/blob/1de46564a766f9647b22ebab0f35bccd14291460/src/runtime/debug/mod.go#L104

Reading of buildinfo, split by control words: https://github.com/mandiant/GoReSym/blob/0860a1b1b4f3495e9fb7e71eb4386bf3e0a7c500/runtime/debug/mod.go#L110-L118

Copy link
Collaborator

@yelhamer yelhamer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@@ -957,6 +960,485 @@ def guess_os_from_symtab(elf: ELF) -> Optional[OS]:
return None


def is_go_binary(elf: ELF) -> bool:
for shdr in elf.section_headers:
if shdr.get_name(elf) == ".note.go.buildid":
Copy link
Collaborator

@yelhamer yelhamer Mar 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The go version command uses logic similar to the one in the get_go_buildinfo_date() function (in addition to looking for the BUILDINFO_MAGIC) to check if an executable is go or not. See references to the errNotGoExe error here: https://github.com/golang/go/blob/master/src/debug/buildinfo/buildinfo.go#L41

Maybe consider incorporating that here? perhaps:

Suggested change
if shdr.get_name(elf) == ".note.go.buildid":
if shdr.get_name(elf) in (".note.go.buildid", ".go.buildinfo"):

Or maybe consider retrieving the buildinfo and testing whether the BUILDINFO_MAGIC is therein? perhaps after we check the section names?

Also, I think this might be a good place to make use of Google's Magika. Maybe if our heuristics can't determine the OS then output the error: "Input file does not appear to target a supported OS according to our heuristics. If you want to use Magika to guess the OS then run capa with the --use-magika parameter"?

@mr-tz
Copy link
Collaborator

mr-tz commented Mar 22, 2024

Tests are TODO / in progress? Getting the following when trying to resolve conflicts:

Failed to merge submodule tests/data (commits don't follow merge-base)
CONFLICT (submodule): Merge conflict in tests/data
Recursive merging with submodules currently only supports trivial cases.
Please manually handle the merging of each conflicted submodule.
This can be accomplished with the following steps:
 - go to submodule (tests/data), and either merge commit 9f7f3c5
   or update to an existing commit which has merged those changes
 - come back to superproject and run:

      git add tests/data

   to record the above merge or update
 - resolve any other conflicts in the superproject
 - commit the resulting index in the superproject

Before continuing, I wanted to confirm what the status here was/is.

@williballenthin
Copy link
Collaborator Author

Yeah, I owe tests here. I believe the implementation is solid but without tests I can't prove it.

At a comfortable effort level, I could have this done in maybe two weeks. If there's a pending deadline, let me know and I can squeeze it in sooner.

@mr-tz
Copy link
Collaborator

mr-tz commented Mar 22, 2024

This is not urgent at all! I just thought I may be able to lend a hand.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Capa v7.01 Not recognizing Linux x64 file
4 participants