Didier Stevens

Monday 19 July 2021

Using SeBackupPrivilege With Python

Filed under: Hacking — Didier Stevens @ 0:00

Access to files on a Windows NTFS filesystem is governed by permissions and privileges.

For permissions, it is done with a security descriptor on a file which contains a Discretionary Access Control List (DACL): these are the permissions that decide if a user has access (and which type of access) to said file. Most files don’t have their own, proper permissions: they inherit them from their parent folders.

Even administrators can be denied access to a file through DACL configuration.

But there is another mechanism that governs access to securable objects like files: privileges. A privilege is a property that a user holds. Administrators have many privileges that normal users don’t have. Like SeBackupPrivilege and SeRestorePrivilege (these are privileges necessary for backup operators).

When a user holds a privilege, it allows that user to do things that other users without that privilege are not allowed to do. For example, the SeBackupPrivilege allows a user to read any file, even if the security descriptor denies access.

But just having the SeBackupPrivilege is not enough:

1) it needs to be enabled programmatically

2) when opening a file, the intention to use the privilege must be specified

Doing this in a programming language like C is easy (for example, I programmed this into my FileScanner tool), but for Python, it’s a bit more complicated.

Part 1, enabling the privilege can be done in Python with the following code (it relies on pywin32).

import win32security
import win32api

def EnablePrivilege(privilege):
    hToken = win32security.OpenProcessToken(win32api.GetCurrentProcess(), win32security.TOKEN_ADJUST_PRIVILEGES | win32security.TOKEN_QUERY)
    win32security.AdjustTokenPrivileges(hToken, 0, [(win32security.LookupPrivilegeValue(None, privilege), win32security.SE_PRIVILEGE_ENABLED)])


Part 2, opening the file, is typically done with WIN32 API function CreateFile and passing it the FILE_FLAG_BACKUP_SEMANTICS flag with argument dwFlagsAndAttributes.

In Python, we usually access files via function open, and not via WIN32 API function CreateFile. We can do that, but I found a simpler method.

Python’s open function has no argument where we can pass flag FILE_FLAG_BACKUP_SEMANTICS, so we cannot use open.

Python also has function os.open, it returns a file descriptor that can then be used with other file descriptor operations, like read. Like open, os.open has no argument to pass flag FILE_FLAG_BACKUP_SEMANTICS. However, someone figured out it can be done indirectly by using flag 0x2000 (os.O_DIRECTORY ?) :

fd = os.open('c:\\demo\\test.txt', 0x2000)
os.read(fd, 0x10) # read 10 bytes

Here under is a demo. File c:\demo\test.txt is only accessible (full control) by a given, normal user. And not by the administrator. This instance of Python is running under the account of an elevated administrator (so that it has the SeBackupPrivilege ready to be enabled).

When attempting to open file c:\demo\test.txt with open and os.open, permission is denied.

But after enabling SeBackupPrivilege, access via os.open is granted:

Saturday 17 July 2021

Update: base64dump.py Version 0.0.16

Filed under: My Software,Update — Didier Stevens @ 0:00

This new version of base64dump.py brings bug fixes and support for BASE85 RFC 1924 encoding.

If you want to know how I go about adding a new decoding to base64dump.py, watch this video:

Here is version, with bug fixes but without base85:

base64dump_V0_0_15.zip (https)
MD5: 95C78B0DC830C6240F2A56A3BA0C483F
SHA256: F011136B2CF4F54647AB4B699CE7F3575925B2BD09EED409E4BBE34FEB8C570A

And here is version with base85:

base64dump_V0_0_16.zip (https)
MD5: 91E283BDF292C463E349DC535EF50535
SHA256: E85345971D209559ED6602F16C6DBBF526816848B2F15B44C06A7DE7B28F2F8C

Friday 16 July 2021

sysmon’s DNS QueryStatus Field

Filed under: Networking — Didier Stevens @ 0:00

A friend asked me for more info on the QueryStatus field in sysmon‘s DNS events.

When a DNS query succeeds, e.g., when there’s a DNS reply with an answer, that status field is 0.

But what can cause it to be different from 0?

A bit of testing revealed that a query for an unknown domain gives a QueryStatus value of 9003. 9003 is a Windows System Error Code for DNS. And the rcode for NXDOMAIN is 3. So maybe the QueryStatus value is the rcode value plus 9000.

I added a feature to my dnsresolver.py script, that allows me to choose the rcode I want to receive. It works with this command-line:

dnsresolver.py “type=rcode,label=rcodetest”

And then I can just do DNS queries for a hostname like this:


When my dnsresolver replies to such a query, it will send a reply without answer and with rcode equal to 4 (because the first label of the DNS query is 4). This allows me to quickly test different rcodes:

And this does indeed confirm that QueryStatus is equal to the rcode (greater than 0) plus 9000.

If the rcode is 0, the QueryStatus is 0, unless there is no answer in the DNS reply. Then the QueryStatus is 9501:

FYI: to test this, I configured a Windows VM with DNS server IP =, ran sysmon and dnsresolver.py inside that VM and did ping requests (I didn’t use nslookup, because that tool talks directly to the DNS server, it doesn’t use the Windows DNS client service).

Thursday 15 July 2021

New Tool: dnsresolver.py

Filed under: My Software,Networking — Didier Stevens @ 0:00

I’ve done several experiments with DNS, which has lead me over the last couple of years to develop a DNS resolver tool.

By no way is it a full fledged DNS server: it implements particular features that I’ve needed for different experiments I conducted.

It can serve files, facilitate exfiltration, do tracking, answer wildcard requests, do rcode testing and also simple resolving.

Upcoming blog posts will go into more details for some of these features.

Example of payload command: serving a file over DNS TXT records
dnsresolver_V0_0_1.zip (https)
MD5: 340C7324EB66EB4F567B38F374DD2564
SHA256: 56AD87585FDCC20C219BF4A27D9640ECD563E4155816990AB4E7B85AAFA5F047

Tuesday 13 July 2021

Update: FileScanner Version

Filed under: My Software,Update — Didier Stevens @ 0:00

This new version of FileScanner brings bug fixes and new features, like UNICODE filename support and an embedded man page.

FileScanner_V0_0_0_7.zip (https)
MD5: D3294BE258F5E2CD9ADF60035D5FB444
SHA256: 8D9349A2056CF400DF55D0407287144A038B6268E40919F248866B4C8BC3FD0A

Monday 5 July 2021

New Tool: texteditor.py

Filed under: My Software — Didier Stevens @ 0:00

I have some ad hoc tools, that help me with special text editing tasks. Like doing search and replace in a text file, with a list of search and replace terms. Or looking for assignment statements in the source code of a program, and replacing each variable with its value.

I decided to bundle these ad hoc scripts, into a single generic script, a new tool: texteditor.py

Here is an example, take text file example.txt with the following content:

There is an apple on the sun.
It is nice and warm.

And CSV file sar.csv with the following content:


The following sarcsv command edits file example.txt with sar.csv:

./texteditor.py input=example.txt edit=sarcsv,file=sar.csv output=-

The output of this command is:

There is an pear on the moon.
It is nice and warm.

texteditor_V0_0_1.zip (https)
MD5: C8BA3AA8E8549B0159B76CF4A520976B
SHA256: D605DF7560C0CDBDCA3F4C1B1DC11EE9765813149B60D144714CF1C6E92CEC22

Sunday 4 July 2021

Update: xmldump.py Version 0.0.7

Filed under: My Software,Update — Didier Stevens @ 0:00

This update to xmldump.py, a tool to help with viewing XML files, adds option -j (–jsoninput) to handle JSON output produced by zipdump.py.

With this option, shared strings from OOXML spreadsheets will be used with command celltext.

I will explain more in an upcoming blog post.

xmldump_V0_0_7.zip (https)
MD5: 20FBBC1A053B2528AC4200B917637876
SHA256: 0D7850CEEDEB7EFD9E8645CF8DD59F1912E9EB3C135346F98AF3E3A7BAAE2B68

Saturday 3 July 2021

Overview of Content Published in June

Filed under: Announcement — Didier Stevens @ 19:01
Here is an overview of content I published in June: Blog posts: YouTube videos: Videoblog posts: SANS ISC Diary entries:

Blog at WordPress.com.