Windows Security Questions stored in the LSA

Table of contents :

Introduction

Windows account security questions were introduced in Windows 10 1803 (in April 2018) to provide users with a means of password recovery.

Storage and format

This secret is stored in the Local Security Authority (LSA) and is named in the format L$_SQSA_<SID>, with:

  • L$ - Local Secret
  • SQSA - Security Questions Security Answers
  • SID - Security identifier

The format of this secret is a character string encoded in UTF-16-LE containing a JSON dictionary:

{
  "version":1,
  "questions": [
    {
      "question":"What was your childhood nickname",
      "answer":"John"
    },
    {
      "question":"What is the first name of your oldest cousin?",
      "answer":"Jack"
    },
    {
      "question":"What was your first pet’s name?",
      "answer":"Fluffy"
    }
  ]
}

Implementation in the tools

Secretsdump of the Impacket suite

In the file impacket/examples/secretsdump.py I added these few lines of python to automatically parse and format the security questions in the output of the secretsdump tool:

elif re.match('^L\$_SQSA_(S-[0-9]-[0-9]-([0-9])+-([0-9])+-([0-9])+-([0-9])+-([0-9])+)$', upperName) is not None:
    # Decode stored security questions
    sid = re.search('^L\$_SQSA_(S-[0-9]-[0-9]-([0-9])+-([0-9])+-([0-9])+-([0-9])+-([0-9])+)$', upperName).group(1)
    try:
        strDecoded = secretItem.decode('utf-16le').replace('\xa0',' ')
        strDecoded = json.loads(strDecoded)
    except:
        pass
    else:
        output = []
        if strDecoded['version'] == 2:
            output.append(" - Version : %d" % strDecoded['version'])
            for qk in strDecoded['questions']:
                output.append(" | Question: %s" % qk['question'])
                output.append(" | └──> Answer: %s" % qk['answer'])
            output = '\n'.join(output)
            secret = 'Security Questions for user %s: \n%s' % (sid, output)
        else:
            LOG.warning("Unknown SQSA version (%s), please open an issue with the following data so we can add a parser for it." % str(strDecoded['version']))
            LOG.warning("Don't forget to remove sensitive content before sending the data in a Github issue.")
            secret = json.dumps(strDecoded, indent=4)

The output of the secretsdump tool now becomes:

I created a pull request to add this feature directly in impacket.

References