Sean Mackert professional headshot

Sean Mackert

- Passionate about security
- Aspiring red teamer
- Seeking mentorship

6-Minute Read


In my experience, .DS_Store is an essential line in your personal scanning lists. These days, exposed .DS_Store files can be rare, but it can be a useful string to use when trying to find hard-to-discover paths as well. Here’s a brief introduction to .DS_Store, and a recount of a time when it really paid off for me.

What is a .DS_Store file?

The .DS_Store file contains folder metadata for Mac users. These files are created automatically inside of archives, local folders, and even remotely mounted folders. More importantly, for us, these files can include file and folder names that we would have otherwise been unable to guess during normal directory bruteforcing techniques.

How can I read a .DS_Store file?

Luckily for us the format has been reversed engineered and you can use this simple one-liner or this Python-dsstore project to parse the file and read the contents. Additionally you can simply read the file with a text editor. You’ll just have to ignore the gibberish bits between the files/folders.

\showItemInfo_viewOptionsVersion_scrollPositionXYarrangeBy]labelOnBottomXiconSize_showIconPreview#?ð      #@K      #@(       #                       Tnone	#@P      	  + A M V k z ‘  ¯ » È Ý ï ù"+4=?HIZ_`i                           j    f i l e s 2vSrnlong       f o n t sIlocblob     ù   .ÿÿÿÿÿÿ      f o n t sbwspblob   ¹bplist00Ö]ShowStatusBar[ShowToolbar[ShowTabView_ContainerShowSidebar\WindowBounds[ShowSidebar		_{{2954, 825}, {880, 750}}	#/;R_klmno‹             
               Œ    f o n t slg1Scomp            f o n t smoDDblob   @ÀOµPìÃA    f o n t smodDblob   @ÀOµPìÃA    f o n t sph1Scomp            f o n t svSrnlong       j q u e r y - 3 . 6 . 0 . m i n . j sIlocblob     g   .ÿÿÿÿÿÿ      j q u e r y . m a s k . j sIlocblob      A   žÿÿÿÿÿÿ  

How is .DS_Store helpful in an engagement?

A quick grep of SeclLists’ Web-Discovery folder reveals that the file is included in all sizes of the file/word raft lists, quickhits.txt, and dirsearch.txt which are among the most popular pre-made bruteforce lists.

$ grep -i '.ds_store' *.txt

However, this file doesn’t always make into the pentester’s personal shortlists, which I believe is a mistake.

Take at this payload position in Burp Intruder which can be used to potentially reveal directories that otherwise would return 404:

GET /§§/.DS_Store HTTP/1.1

This might be appear to be useless unless a .DS_Store file actually exists, but let’s take a closer look at our request/response behavior on this particular machine.

Advanced directory discovery.

Searching for the directory geoip as normal returns a 404 error— so it must not exist, right?:

GET /geoip/ HTTP/1.1
HTTP/1.1 404 Not Found
content-type: text/plain
content-length: 18
connection: close

404 page not found

Let’s try once more but look for a specific filename that surely doesn’t exist on the server:

GET /geoip/d035n073X157.txt HTTP/1.1
HTTP/1.1 403 Forbidden
content-length: 0
connection: close

Now we hit a 403 Forbidden error. We know that sensitive files like .htaccess or config files ending in .ini will often return a 403, but this this file doesn’t exist so either the server settings forbid the requesting of .txt files, or we have wandered into someplace we don’t belong and got a bit lucky that the server is setup to send 403s instead of 404s.

Let’s remove the extension and see:

GET /geoip/d035n073X157 HTTP/1.1
HTTP/1.1 403 Forbidden
content-length: 0
connection: close

Yahtzee! It looks like we’ve really discovered a new directory. Let’s quickly double check our work before bombarding the client with 30,000 more requests for a false-positive URI!

GET /geoip1/d035n073X157 HTTP/1.1
HTTP/1.1 404 Not Found
content-type: text/plain
content-length: 18
connection: close

404 page not found

Excellent! We’ve appended a character (1) to the directory name and requested the same non-existent file and received a 404 Not Found error instead of the 403. Now I’m confident that geoip is a valid directory. But first, let’s find some more interesting directories…

In search of a .DS_Store that doesn’t exist.

But what if we simply used the .DS_Store file the entire time? Well, first we should do some due diligence checks.

Does the server return a 403 error for all dot files?

GET /.DS_Store HTTP/1.1
HTTP/1.1 404 Not Found
content-type: text/plain
content-length: 18
connection: close

404 page not found

Our request returned at 404 instead of a 403 meaning that the server isn’t configured to automatically deny all requests to a file with a leading dot.

But what about the rules for our known good directory path specifically?

GET /geoip/.d035n073X157 HTTP/1.1
HTTP/1.1 404 Not Found
content-type: text/plain
content-length: 18
connection: close

404 page not found

Another 404. It looks like there is no configuration or WAF preventing us from requesting dot files anywhere.

Let’s go ahead and scan for more directories using this technique. We’ll keep a lookout for 403 errors indicating that we’re on a valid path; and our fingers crossed for a 200! Our Intruder configuration will be:

GET /§§/.DS_Store HTTP/1.1

And we quickly receive a hit. Requesting /banks/.DS_Store gives us a 403. Unfortunately, scanning for sub directories gives us nothing but 404s, with and without using our .DS_Store trick.

Let’s go back to the basics and make a short list of well-known banks that serve our target’s geographical market and run it through Burp Intruder again.

HTTP/1.1 200 OK
Content-Length: 10244
Etag: "ta8b3p2bl"
Connection: close


Wow, we’ve actually found a .DS_Store file in the wild! Next we should start a scan in the folder for other files and then take a look at our loot.

Our scan revealed the following files:

/main.css             (Status: 200) [Size: 1022]
/print.css            (Status: 200) [Size: 660]
/common.js            (Status: 200) [Size: 10349]
/2.jpg                (Status: 200) [Size: 20673]
/main.js              (Status: 200) [Size: 962]
/1.jpg                (Status: 200) [Size: 31282]
/lightbox.css         (Status: 200) [Size: 6737]
/3.jpg                (Status: 200) [Size: 17913]
/4.jpg                (Status: 200) [Size: 9771]
/a.js                 (Status: 200) [Size: 137964]
/anchor.htm           (Status: 200) [Size: 23290]
/ext.js               (Status: 200) [Size: 4617]

And in .DS_Store we find:


Hey, that’s a big help! We could have never bruteforced many of the additional files found. A lot of the files are minified versions and lazily-loaded parts, but because this app isn’t (supposed to be) a public facing interface we should thoroughly check these files for hardcoded API keys, API endpoints, and developer notes.

In essence, there is little reason why not to use .DS_Store (provided you’ve done your due diligence) as the target file for subdirectory scanning. Specifically, in environments where the server returns 403s for folders but not files or you are scanning and guessing multiple folders deep — places where developers don’t expect you to be — its a great tool to turn a shot-in-the-dark into something useful. If your environment permits it, you can save time waiting for scan results and potentially reveal files and folders that would not be otherwise discovered.

Happy hunting!

Recent Posts



Sean Mackert is an IT professional passionate about security and helping inform others.