2

Consider an NTFS folder that has the explicit permissions Create Files / Write Data applied onto This Folder Only, and Create Folders / Append Data applied onto Files Only (and Read Attributes, because I understand that to be necessary).

> icacls x:\pathto\folder
x:\pathto\folder CONTOSO\Domain Users:(CI)(S,WD)
                 CONTOSO\Domain Users:(OI)(IO)(S,AD,REA,RA)
                 BUILTIN\Administrators:(I)(OI)(CI)(F)
                 NT AUTHORITY\SYSTEM:(I)(OI)(CI)(F)

In theory, this should allow a user to create a file if it doesn't exist, and append to a file if it does exist, but not otherwise overwrite or truncate the contents of the file, nor delete it.

Now, given that, how does one actually open that file for appending?

I've tried simple command-line/batch redirection:

> echo foo >> x:\pathto\folder\thefile.txt
> echo foo >> x:\pathto\folder\thefile.txt
Access denied.

...and VBScript

Dim fso : Set fso = CreateObject("Scripting.FileSystemObject")
Dim stream : Set stream = fso.OpenTextFile("x:\pathto\folder\thefile.txt", 8, True, 0)  ' 8-ForAppending
stream.WriteLine("foo")
-----
> cscript writefoo.vbs //nologo //e:vbs
> cscript writefoo.vbs //nologo //e:vbs
Microsoft VBScript Runtime Error(2, 14): Permission denied

...and .NET (via PowerShell)

> (1..2) | %{
>   Write-Host $_
>   $f = [IO.File]::AppendText('x:\pathto\folder\thefile.txt')
>   $f.WriteLine("foo")
>   $f.Dispose()
> }
1
2
Exception calling "AppendText" with "1" argument(s): Access to the path "x:\pathto\folder\thefile.txt" is denied.

...and another .NET approach (again via PowerShell)

> (1..2) | %{
>   Write-Host $_
>   $f = [IO.File]::Open('x:\pathto\folder\thefile.txt', [IO.FileMode]::Append, [IO.FileAccess]::Write, [IO.FileShare]::None)
>   $f = New-Object IO.StreamWriter -Arg $f
>   $f.WriteLine("foo")
>   $f.Dispose()
> }
1
2
Exception calling "Open" with "4" argument(s): Access to the path "x:\pathto\folder\thefile.txt" is denied.

In all cases, with Sysinternals Procmon running, I can see that the underlying CreateFile is always made requesting Generic Write for its permissions.

If I understand correctly, Generic Write means Win32's FILE_GENERIC_WRITE, which is a bitflag combination of FILE_APPEND_DATA | FILE_WRITE_DATA (and others) which would require Create Files / Write Data on the file as well as the directory.

However, if I place Create Files / Write Data on the file, then files are no longer append-only -- they can't be deleted and re-written, but they can be truncated and rewritten -- which defeats the purpose.

So what mechanisms are available to me to open the file really, really for Append only?

jimbobmcgee
  • 1,561
  • 11
  • 34
  • 1
    write attributes permmission. may be…https://superuser.com/questions/745923/ntfs-permissions-create-files-and-folder-but-prevent-deletion-and-modification or https://www.itprotoday.com/strategy/ntfs-secrets – elzooilogico May 31 '19 at 18:45
  • The .h file says only local files and doesn't mention network files. For a file object, the right to append data to the file. (For local files, write operations will not overwrite existing data if this flag is specified without FILE_WRITE_DATA.) For a directory object, the right to create a subdirectory (FILE_ADD_SUBDIRECTORY). – Noodles Jun 01 '19 at 02:55

2 Answers2

3

You really should only open files with Append Data. Difference between FILE_WRITE_DATA and FILE_APPEND_DATA.

echo and other methods cannot append data with only Append Data access(There are other complicated access inside. according to your test), but why not create a "Append" command(like echo) directly using CreateFile? Access Privileges can be strictly controlled with CreateFile.

Append.cpp:

#include <windows.h>
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
    if (argc != 3)
        return 0;
    //cout << argv[1] << "," << argv[2] << endl;
    HANDLE hFile = CreateFile(argv[2], FILE_APPEND_DATA, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    WriteFile(hFile,argv[1],strlen(argv[1]),0,0);
    CloseHandle(hFile);
}

Then copy the Append.exe to the folder where you want to execute echo or "C:\Windows\System32". Use the it like command:

Append foo x:\pathto\folder\thefile.txt
Drake Wu
  • 6,927
  • 1
  • 7
  • 30
1

Figured it out, at least for PowerShell/.NET. The .NET FileStream object offers a constructor which takes a FileSystemRights enum parameter, which lets you explicitly limit the underlying CreateFile call not to require Generic Write.

> (1..3) | %{
>   Write-Host $_
>   $f = New-Object -TypeName 'IO.FileStream' `
>                   -ArgumentList $private:file,`
>                                 ([IO.FileMode]::Append),`
>                                 ([Security.AccessControl.FileSystemRights]::AppendData),`
>                                 ([IO.FileShare]::Read),`
>                                 4KB,`
>                                 ([IO.FileOptions]::None)
>   $f = New-Object -TypeName 'IO.StreamWriter' `
>                   -ArgumentList $f
>   $f.WriteLine("foo")
>   $f.Dispose()
> }
1
2
3

No options that I know of for VBScript or Batch (beyond Drake's suggestion of rolling one's own COM type or append.exe, which calls CreateFile directly via C++/Win32).

jimbobmcgee
  • 1,561
  • 11
  • 34