Thursday, January 25, 2024

Getting Go Linker Flags from the Executable in a Docker Image

In between the holidays, I found myself working on a task, for which I needed some IDs to test my code. As my colleagues were not available, I felt challenged to find them on my own. In what comes, I will describe what I did.


1. I checked where the variable was coming from.

I looked at the source code written in Go, and I saw it was an environment variable, so naturally, I checked the docker image with:

$ docker inspect

No environment variables were set, besides some basic PATHs.

I checked the code further, looking at the GitHub Actions configured, and I realized they came from the ldflags in the GoReleaser.


2. I looked up ldflags.

As I did not need to use them before, I googled ldflags. Apparently, they are used in linkers, the tools that make sure multiple source code files can work together - including the dependencies. ldflags are used to inject values during compilation. They are useful to mark the time and version of the build, and bake in other special values.


3. I checked how I could get ldflags back from executables.

I first found the Linux command:

$ nm


4.  I tried running the Docker image and getting a terminal to interact with the built object.

I used the command

$ docker run --entrypoint /bin/sh -it sha256:c2dc7194677d97676e...

It did not work, `/bin/sh` was missing from the image. Upon investigating the Dockerfile, I saw that this image was built from scratch. Good security, bad for my debugging purposes.


5. I built a new Docker image.

I knew where the path was in the original image, so I wanted to build a new image based on a Docker image having tools to debug. I used the go base image, and copied over the file in the new Dockerfile:


FROM golang:1.21

COPY --from=${ORIGINAL_IMAGE}:${VERSION} ${ORIGINAL_EXECUTABLE_PATH} ${TARGET_PATH}

CMD ["go","tool","nm","${TARGET_PATH}"]


I the built and ran the image.

$ docker build ...

$ docker run ...

No response.


6. I exec-ed into the Docker container.

I used the following command to exec into the newly built image's container:

$ docker run --entrypoint /bin/sh -it sha256:c2dc7194677d97676e...


7. I used the nm command.

$ nm ${TARGET_PATH}

nm: ${TARGET_PATH}: no symbols


Then

$ nm -D ${TARGET_PATH}

nm: ${TARGET_PATH}: no symbols


8. As this did not help me, I looked further and found that Go has its own version.

$ go tools nm

But this did not give me what I needed either.


9. Finally, I found a StackOverflow entry and executed the strings command from it, which literally looks at the strings in a file.

$ strings ${TARGET_PATH} | grep "${VARIABLE_NAME}"

This command finally returned the sensitive values I needed, and I could move on with testing the feature in my local environment.


Normally, I would advise that you ask your colleagues instead of investigating on your own, but you might find yourself in the same trouble as I did. It was still a fun exercise, and it was nice learning about the linker, flags, and the different commands you can use to investigate an executable.

No comments:

Post a Comment

Building Resilience

In the beginning of the year we had a conversation with my manager about my yearly goals. Due to the nature of my current project and the ne...