{
	"id": "f7a6a53c-e4db-4667-8e6f-6757d873aeb3",
	"created_at": "2026-04-06T00:09:48.553813Z",
	"updated_at": "2026-04-12T02:22:33.846692Z",
	"deleted_at": null,
	"sha1_hash": "bd77fdf1365561654721453cbf3b0d27d1afcd0f",
	"title": "SensePost | Outlook Forms and Shells",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 365359,
	"plain_text": "SensePost | Outlook Forms and Shells\r\nArchived: 2026-04-05 17:07:58 UTC\r\nUsing MS Exchange and Outlook to get a foothold in an organisation, or to maintain persistence, has been a go to\r\nattack method for RedTeams lately. This attack has typically relied on using Outlook Rules to trigger the shell\r\nexecution. Although Ruler makes accomplishing this really easy, it has, up until now, required a WebDAV server\r\nto host our shell/application. In most cases this is not an issue, but once in a while you run into a restrictive\r\nnetwork that does not allow the initial WebDAV connection to be established. In such instances, the attack sadly\r\nfails. Another downside to Outlook rules is that we are limited to only providing an application path, and no\r\ncommand-line arguments, meaning none of our fancy Powershell one-liners can be used.\r\nLastly, Microsoft has released a patch for Outlook 2016 that disables both our Run Application and Run Script\r\nrules by default.\r\nIt was time to find a new attack avenue.\r\nAttack Surface\r\nI set out to try and find another way to get a shell through Outlook, in the case of us having valid credentials. The\r\nfirst interesting angle would be to use the VBA Macro engine built into Outlook. Unfortunately, this is a no go for\r\na few reasons. Firstly, VBA Macros are not synchronised between Outlook instances, unlike rules. Secondly, as\r\nmentioned above, Run Script rules are not going to be available going forward. And lastly, more and more\r\norganisations are (finally) moving towards a “block all macros” policy.\r\nFortunately for us, Outlook has a massive attack surface and provides several other interesting automation\r\nfeatures. One of these is Outlook Forms. Forms provide a user/organisation with email customisation options on\r\nhow it is presented or composed. This includes features such as autocompleting the bcc field or inserting template\r\ntext.\r\nAll Outlook message objects are actually forms in their own right. The only difference between an Appointment\r\nRequest message and a normal Message, is the form used to display these in the Outlook UI.\r\nAppointment Message, using IPM.Appointment\r\nhttps://sensepost.com/blog/2017/outlook-forms-and-shells/\r\nPage 1 of 9\n\nMessage form, your standard IPM.Note\r\nNow, this is interesting: you can change the way a message appears or what fields are available to a user when\r\ncomposing a new message. More information about forms can be viewed directly from the source, Microsoft:\r\nhttps://msdn.microsoft.com/en-us/library/office/ff868929.aspx\r\nWhat if you want to create your own form? It is pretty simple, you need to enable the Developer tab in the ribbon,\r\nand select Design a Form. This opens a form designer where you can move and edit various form fields.\r\nSelecting a form to design. This can be based off of existing forms.\r\nThe form designer.\r\nWhile looking at this designer, an interesting little area caught my attention. If you hover over the icon with a\r\nform and magnifying-glass you will get a pop-up message View Code – Edit the Visual Basic code for a\r\ncontrol. You may now figure where this is going…\r\nhttps://sensepost.com/blog/2017/outlook-forms-and-shells/\r\nPage 2 of 9\n\nThe option to edit VBScript\r\nOnce you open up the code viewer, you will see a very basic text editor with two options under the script menu,\r\nnamely Event Handler and Object Browser. That is it, you need to figure out the rest by yourself (unlike the nice\r\nVBA editor that ships with Office). Selecting the Event Handler pick the open option and VBScript will be\r\ninserted to handle the on_open event for this form.\r\nCreating some script\r\nIf you close the script editor, you can now run the form. This is done by using the Run this form button, found\r\nright below the View Code button. Immediately a MsgBox will pop up, along with the new form!\r\nI had disabled Macros in Outlook, so how could this code be running?!\r\nIt turns out that this script engine is separate from the VBA Macro script engine, as explained\r\nhere: http://drglennn.blogspot.co.uk/2008/07/vbscript-and-custom-forms-in-outlook.html.\r\nThis means, we have a full VBScript engine available to us, and can now start trying to insert a more juicy\r\npayload.\r\nA test payload of:\r\nFunction Item_Open()\r\n CreateObject(\"Wscript.Shell\").Run \"calc.exe\", 0, False\r\nEnd Function\r\nYields a nice calculator as soon as the form is opened. This can be extended to perform different actions when a\r\nreply is created, a message is forwarded, etc. Winning, so far. Now the big test, does it persist and does it\r\nhttps://sensepost.com/blog/2017/outlook-forms-and-shells/\r\nPage 3 of 9\n\nsynchronise?\r\nSave a form\r\nForms can either be saved to a local file (Save-As) which then makes it available to the local install of Outlook,\r\nnot very useful for us, but you can also Publish a form. When publishing, the form can be stored in the Personal\r\nForms Library or in an Outlook folder, such as the Inbox.\r\nThe form can be published to an Exchange folder.\r\nOnce published, the form gets a new form name and its own message class. The message class is important, as\r\nOutlook will use this to determine which form to use when processing a message and displaying it. If the form\r\ngets published into a folder such as the Inbox, it automatically gets synchronised with the Exchange server,\r\nand all instances of Outlook associated to that account!\r\nAfter the form is saved, you need to be able to activate/trigger it somehow. This can be done directly through the\r\nOutlook interface, but this is not very useful, as you will be shelling yourself. If you want to test if a form works\r\nas you would like it to, select the inbox and then in the ribbon select New Items -\u003e Custom Forms -\u003e Form_name.\r\nThe new form can be accessed through the menu.\r\nhttps://sensepost.com/blog/2017/outlook-forms-and-shells/\r\nPage 4 of 9\n\nHow about triggering this remotely? For this you need to send an email of the correct message class. To do this\r\nthrough Outlook, you will need to create a form with the same message class as the one you have created on your\r\ntarget’s mailbox, and then send an email using that form. Convoluted, I know.\r\nAutomating – Ruler\r\nAs demonstrated by the last example of how to trigger the email, it is rather complicated trying to do this through\r\nOutlook. Also creating the malicious form in Outlook, would require you to synchronise the mailbox to your own\r\nhost. This seemed an ideal feature to extend the functionality in Ruler and create these forms automatically.\r\nNo Documentation\r\nThe first issue in doing this was figuring out where or how these forms are actually stored. When developing\r\nRuler I was fortunate enough to have specific MAPI Remote Operations (ROPS) available to interact with the\r\nRules Organiser in Exchange. Sadly, with forms I was not so lucky. Turns out forms do not have their own ROPS,\r\nand documentation of how these are created/stored/accessed through MAPI is near non-existent, as far as I know.\r\nFortunately, there is a brilliant application we can use to view a Mailbox and all MAPI objects associated with that\r\nmailbox. MFCMAPI, tool that provides access to MAPI stores to facilitate investigation of Exchange and\r\nOutlook issues – https://github.com/stephenegriffin/mfcmapi – if you are serious about looking into MAPI\r\ndevelopment, this is the tool to turn to.\r\nDiscovery\r\nUsing MFCMAPI I was able to determine that forms get stored in the associated table for a folder. The associated\r\ntable is described as “A collection of Message objects that are stored in a Folder object and are typically hidden\r\nfrom view by email applications. A FAI Message object is used to store a variety of settings and auxiliary data,\r\nincluding forms, views, calendar options, favourites, and category lists.” \r\nLooking at the associated folder using MFCMAPI we can find the form message. This is saved with a message\r\nclass of IPM.Microsoft.FolderDesign.FormsDescription. Each form is then described in the attachments and\r\nProperyTags stored with these messages. There are a bunch of options that need to be set for each form, and these\r\nare controlled through the PropertyTags of the message.\r\nWeirdly enough, PropertyTags such as PidTagOfflineAddressBookName are used to describe the form, there\r\naren’t any PidTag[form] PropertyTags. This particular tag controls the message class of the form – in this case\r\nIPM.Note.pewpew. The form document, along with the VBScript to execute is saved in an unique attachment, the\r\ndata of this form is stored in the Outlook message format, basically the same format used when you save a\r\nmessage to disk.\r\nhttps://sensepost.com/blog/2017/outlook-forms-and-shells/\r\nPage 5 of 9\n\nInspecting the associate table with MFCMAPI\r\nNow that I knew where the forms where stored, I had to figure how to create them. The MAPI library I created for\r\nRuler had all the functions needed to create messages, attachments and custom PropertyTags. The complicated\r\npart was deciphering exactly which tags are required and what the different values mean (we want dynamic forms\r\nafter all). This took a few painful hours of creating messages and comparing the Ruler created message with that\r\ngenerated in Outlook. Eventually I stumbled on all the required values.\r\nIt also allowed for the discovery of some nice “hidden” features. For example, setting the\r\nPidTagSendOutlookRecallReport to true would hide the form from the user interface. This means the new form\r\nwon’t show up under custom forms in the new item menu. To discover the new form,  a user would need to go into\r\nthe advanced options tab in Outlook, navigate to forms, select the inbox and view the list of forms (unlikely).\r\nThe inbox is also not the default forms library location, thus users who do modify forms, tend to only see those\r\nstored in the Personal Forms Library, which is the default view and storage space.\r\nCustom Form\r\nBeing able to create a form through Ruler was great, but how about specifying a custom payload, custom\r\nVBScript? This got a little complicated.\r\nOption one was to sift through the MS-OXMSG.pdf and create a custom parser/generator for the MSG format;\r\noption two was to hexedit an existing form. I went for the latter.\r\nThe current form script template in use:\r\nFunction P()\r\n\u003e\u003eMAGIC\u003c\u003c\r\nEnd Function\r\nFunction Item_Open()\r\nCall P()\r\nEnd Function\r\nFunction Item_Reply(ByVal Response)\r\nCall P()\r\nEnd Function\r\nFunction Item_Forward(ByVal ForwardItem)\r\nCall P()\r\nEnd Function\r\nhttps://sensepost.com/blog/2017/outlook-forms-and-shells/\r\nPage 6 of 9\n\nFunction Item_Read(ByVal Response)\r\nCall P()\r\nEnd Function\r\nWhen Ruler generates the new form, it searches through form template for our “MAGIC” token and replaces this\r\nwith the supplied payload. For now, this means that the payload size is limited to 4096 bytes, which should be\r\nmore than enough for creating a useful payload. This means the standard base64 encoded Empire launcher should\r\nfit. But we are hackers, with 4096 bytes you should be able to do A LOT of interesting things.\r\nYou should also notice that there are multiple triggers for this form. The payload will be called if the message is\r\nread (previewer), opened (not previewed) or if the user attempts to reply or forward the message. This means that\r\nthe user needs to at least preview the message. Alternatively you need a slight amount of social engineering,\r\nwhere the attacker needs to either get the user to open the message or to reply to it. A nice side affect is that the\r\nuser will inadvertently trigger the payload if they try “forward” it to the incident response team.\r\nAttack Attack\r\nA few changes to Ruler and the new attack is baked in. The new functionality can be accessed through the form\r\ncommand.\r\n./ruler --email john@msf.com form help\r\nUSAGE:\r\nruler form [global options] command [command options] [arguments…]\r\nVERSION:\r\n2.0.17\r\nCOMMANDS:\r\nadd creates a new form.\r\nsend send an email to an existing form and trigger it\r\ndelete delete an existing form\r\ndisplay display all existing forms\r\nUnder the form command, there are a number of sub commands. Just as you have with standard Ruler. To add a\r\nnew form:\r\n./ruler --email john@msf.com form add --suffix pewpew --command \"MsgBox(\\\"hello\\\")\" --send\r\nhttps://sensepost.com/blog/2017/outlook-forms-and-shells/\r\nPage 7 of 9\n\nIn this example, this will create a new form, with the message class IPM.Note.pewpew and the VBScript to show\r\nthe MsgBox. The command can also be supplied from a file, using the –input argument.\r\nLeaving out the –command or –input arguments will provide with a sample VBScript to use.\r\nThe –send specifies that we want a trigger mail to be sent. The default email has a subject of “Invoice\r\n[Confidential]” and the body “This message cannot be displayed in the previewer”. If you wish to customise this\r\nemail, simply use –subject “new subject” and –body “new body”.\r\nTo trigger an existing form, you can use the send command.\r\n./ruler --email john@msf.com form send --suffix pewpew\r\nAnd same story with the subject and body. Forms can also be retrieved and deleted. If you wish to view a list of\r\ncurrent forms, use the display command and to delete a form, delete –suffix pewpew.\r\nThe end result of this can be seen here: https://youtu.be/XfMpJTnmoTk\r\nEtt fel inträffade.\r\nDet går inte att köra JavaScript.\r\nCode\r\nThe new version of Ruler is available on Github: https://github.com/sensepost/ruler\r\nNOTE: If you are going to use the pre-built binaries, there is an extra setup step!\r\nIn your current working directory, you need a folder named “templates”. In this folder you will require the files:\r\nimg0.bin\r\nimg1.bin\r\nformtemplate.bin\r\nhttps://sensepost.com/blog/2017/outlook-forms-and-shells/\r\nPage 8 of 9\n\nThese can be retrieved from the github folder: https://github.com/sensepost/ruler/tree/master/templates\r\nPersistence\r\nWhat I really like about this method is the fact that it is ideal for persistence. If you compromise an account,\r\ninstall a custom form and that is it. If you lose access to that account, all you need to do to trigger the shell again is\r\nto send an email with the correct message class. Unlike rules, there is not an easy way in the UI for a user to check\r\ntheir forms. And as far as I can see, forms can not be seen in OWA, unlike Rules. Because the message is saved in\r\nthe associated table, the standard query tools for Exchange do not allow you to retrieve this information.\r\nDefence\r\nApart from MFA/2FA, defending against this is going to be interesting, just like Rules, I cannot give a firm “this\r\nwill block it” solution.\r\nGood monitoring and logging should be able to pickup the VBScript execution. Unfortunately, based on my\r\ntesting, disabling macros does not protect against this. I have not tried with macros disabled via GPO, but with\r\nDisable all macros without notification set in Outlook, the VBScript will still execute.\r\nThis setting doesn’t seem to help.\r\nIf there are other ways of blocking VBScript in Outlook, please share and I will update this post.\r\nFuture Work\r\nDepending on time and the popularity of this specific method of getting shell, I will look into better customisation\r\noptions for the supplied script. This is going to take some more work, but the idea is to implement some logic that\r\nallows the attacker to choose when the script should trigger, either on open, reply, forward, etc, or all of them.\r\nSource: https://sensepost.com/blog/2017/outlook-forms-and-shells/\r\nhttps://sensepost.com/blog/2017/outlook-forms-and-shells/\r\nPage 9 of 9",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"MITRE"
	],
	"origins": [
		"web"
	],
	"references": [
		"https://sensepost.com/blog/2017/outlook-forms-and-shells/"
	],
	"report_names": [
		"outlook-forms-and-shells"
	],
	"threat_actors": [],
	"ts_created_at": 1775434188,
	"ts_updated_at": 1775960553,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/bd77fdf1365561654721453cbf3b0d27d1afcd0f.pdf",
		"text": "https://archive.orkl.eu/bd77fdf1365561654721453cbf3b0d27d1afcd0f.txt",
		"img": "https://archive.orkl.eu/bd77fdf1365561654721453cbf3b0d27d1afcd0f.jpg"
	}
}