Thought process: find where the PDF could be tampered with.
For metadata I used ExifTool:
exiftool confidential.pdf
ExifTool Version Number : 13.50 File Name : confidential.pdf Directory : . File Size : 183 kB File Modification Date/Time : 2026:02:23 19:05:32+08:00 File Access Date/Time : 2026:02:23 19:14:25+08:00 File Inode Change Date/Time : 2026:02:23 19:10:39+08:00 File Permissions : -rw-r--r-- File Type : PDF File Type Extension : pdf MIME Type : application/pdf PDF Version : 1.7 Linearized : No Page Count : 1 Producer : PyPDF2 Author : cGljb0NURntwdXp6bDNkX20zdGFkYXRhX2YwdW5kIV8zNTc4NzM5YX0=
I noticed the Author value looked like Base64 (padded with =), so I decoded it:
echo "cGljb0NURntwdXp6bDNkX20zdGFkYXRhX2YwdW5kIV8zNTc4NzM5YX0=" | base64 --decode
This returned: picoCTF{puzzl3d_m3tadata_f0und!_3578739a}
First thought: search for picoCTF in logs.
strings server.log | grep picoCTF
[1990-08-09 10:00:10] INFO FLAGPART: picoCTF{us3_
[1990-08-09 11:04:27] INFO FLAGPART: picoCTF{us3_
[1990-08-09 11:04:29] INFO FLAGPART: picoCTF{us3_
[1990-08-09 11:04:37] INFO FLAGPART: picoCTF{us3_
[1990-08-09 12:19:23] INFO FLAGPART: picoCTF{us3_
[1990-08-09 12:19:29] INFO FLAGPART: picoCTF{us3_
[1990-08-09 12:19:32] INFO FLAGPART: picoCTF{us3_That only gave me the start. Next I searched for the closing brace to get the end:
strings server.log | grep '}'
[1990-08-09 10:10:54] INFO FLAGPART: cedfa5fb} [1990-08-09 10:10:58] INFO FLAGPART: cedfa5fb} [1990-08-09 10:11:06] INFO FLAGPART: cedfa5fb} [1990-08-09 11:16:58] INFO FLAGPART: cedfa5fb} [1990-08-09 11:16:59] INFO FLAGPART: cedfa5fb} [1990-08-09 11:17:00] INFO FLAGPART: cedfa5fb} [1990-08-09 12:28:45] INFO FLAGPART: cedfa5fb} [1990-08-09 12:28:49] INFO FLAGPART: cedfa5fb} [1990-08-09 12:28:52] INFO FLAGPART: cedfa5fb}
Combining those gave picoCTF{us3_cedfa5fb}, which was wrong. I realized I was missing the middle in snake_case, so I searched for underscores:
strings server.log | grep '_'
[1990-08-09 10:00:10] INFO FLAGPART: picoCTF{us3_
[1990-08-09 10:02:55] INFO FLAGPART: y0urlinux_
[1990-08-09 10:05:54] INFO FLAGPART: sk1lls_
[1990-08-09 10:05:55] INFO FLAGPART: sk1lls_
[1990-08-09 11:04:27] INFO FLAGPART: picoCTF{us3_
[1990-08-09 11:04:29] INFO FLAGPART: picoCTF{us3_
[1990-08-09 11:04:37] INFO FLAGPART: picoCTF{us3_
[1990-08-09 11:09:16] INFO FLAGPART: y0urlinux_
[1990-08-09 11:09:19] INFO FLAGPART: y0urlinux_
[1990-08-09 11:12:40] INFO FLAGPART: sk1lls_
[1990-08-09 11:12:45] INFO FLAGPART: sk1lls_
[1990-08-09 12:19:23] INFO FLAGPART: picoCTF{us3_
[1990-08-09 12:19:29] INFO FLAGPART: picoCTF{us3_
[1990-08-09 12:19:32] INFO FLAGPART: picoCTF{us3_
[1990-08-09 12:23:43] INFO FLAGPART: y0urlinux_
[1990-08-09 12:23:45] INFO FLAGPART: y0urlinux_
[1990-08-09 12:23:53] INFO FLAGPART: y0urlinux_
[1990-08-09 12:25:32] INFO FLAGPART: sk1lls_Using timestamps, I reconstructed: picoCTF{us3_y0urlinux_sk1lls_cedfa5fb}.
In hindsight, searching directly for FLAGPART would have been easier.
First, I verified the file type:
file img.jpg
img.jpg: JPEG image data, JFIF standard 1.01, aspect ratio, density 1x1, segment length 16, comment: "c3RlZ2hpZGU6Y0VGNmVuZHZjbVE9", baseline, precision 8, 640x640, components 3
The comment looked suspicious, so I decoded it:
echo "c3RlZ2hpZGU6Y0VGNmVuZHZjbVE9" | base64 --decode
steghide:cEF6endvcmQ=
That hinted at using steghide. I decoded the second Base64 string:
echo "cEF6endvcmQ=" | base64 --decode
pAzzword
I used it as the extraction password:
steghide extract -sf img.jpg -p pAzzword
Recovered flag: picoCTF{h1dd3n_1n_1m4g3_1c55ccd0}.
I also tried binwalk, but it returned nothing because this challenge hides encrypted data in pixels rather than embedded file signatures.
I first checked if the file was really text:
file logs.txt
logs.txt: ASCII text, with very long lines (65536), with no line terminators
Searching for a direct flag string did not work:
strings logs.txt | grep picoCTF
So I inspected raw bytes for hidden signatures:
hexdump -C logs.txt | head
00000000 69 56 42 4f 52 77 30 4b 47 67 6f 41 41 41 41 4e |iVBORw0KGgoAAAAN| 00000010 53 55 68 45 55 67 41 41 41 34 41 41 41 41 53 41 |SUhEUgAAA4AAAASA| 00000020 43 41 49 41 41 41 41 68 38 62 53 4f 41 41 45 41 |CAIAAAAh8bSOAAEA| 00000030 41 45 6c 45 51 56 52 34 6e 4f 7a 39 31 39 4d 73 |AElEQVR4nOz919Ms| 00000040 79 5a 55 6e 69 50 33 4f 63 59 2b 49 46 4a 2b 36 |yZUniP3OcY+IFJ+6| 00000050 6f 75 71 57 42 71 6f 61 75 74 44 59 52 6d 4e 36 |ouqWBqoautDYRmN6| 00000060 65 6d 66 61 5a 6d 6c 63 30 6d 69 32 2b 37 78 50 |emfaZmlc0mi2+7xP| 00000070 4e 4a 4a 2f 47 4a 2f 34 52 76 4a 74 48 35 64 6d |NJJ/GJ/4RvJtH5dm| 00000080 51 31 76 62 48 75 34 4d 70 39 6b 7a 6a 51 59 47 |Q1vbHu4Mp9kzjQYG| 00000090 6f 68 75 6f 41 6b 72 68 31 74 57 66 79 73 79 49 |ohuoAkrh1tWfysyI|
The starting bytes indicated a Base64-encoded PNG header. I decoded the file:
cat logs.txt | base64 --decode > discovered_image.png
In the PNG, I found this hex sequence:
7069636F4354467B666F72656E736963735F616E616C797369735F69735F616D617A696E675F35636363376362307D
It matched hex-byte characteristics (0-9, A-F, even length). Converting to ASCII:
echo "7069636F4354467B666F72656E736963735F616E616C797369735F69735F616D617A696E675F35636363376362307D" | xxd -r -p
Final flag: picoCTF{forensics_analysis_is_amazing_5ccc7cb0}.