BLOG POSTS
Brute-Forcing Borrowers' PINs at the Swedish Board of Student Finance (CSN)
By Laban Sköllermark (@LabanSkoller)
The Swedish Board of Student Finance CSN is the government agency that manages Swedish student finance, i.e. grants and loans for studies. They also manage driving licence loans and home equipment loans. (Source)
This is the story of when I found two security vulnerabilities in their login functionality and reported it to them.

Timeline
Date | Event |
---|---|
2020-AUG-04 | Problems found |
2020-AUG-05 | Discussions with CSN how to report |
2020-AUG-06 | Vulnerability report sent to CSN |
2020-AUG-06 | Reception of report confirmed by CSN |
2020-AUG-07 | Asked specifically about permission to perform a proof-of-concept against myself (still no response) |
2020-SEP-24 | Email from the Information Security Officer at CSN thanking me for the report. They are working on mitigations for the two vulnerabilities. |
2020-SEP-25 | Asked again for permission to perform a proof-of-concept. I also asked if they think the mitigations will be finished before the planned publication the 4th of November. Still no response. |
2020-NOV-04 | 90 days have passed since CSN received the vulnerability report. The original plan was to publish this post this day but I procrastinated it... |
2020-NOV-14 | CSN start two days of My Pages maintenance over the weekend. The vulnerabilities seem to be unsolved. |
2020-NOV-22 | A preview of this blog post was sent to all people mentioned in it for review (including CSN) |
2020-NOV-23 | CSN take down the vulnerable page for maintenance right before the publication |
2020-NOV-23 | Publication of this post at 17:06 CET |
Catching My Attention
I’m member of a Swedish speaking Facebook group around IT security called Säkerhetsbubblan with 6800 members. One post by the group member Shadi Domat the 4th of August 2020 was regarding CSN’s use of a four-digit PIN as one way of logging in (link to the post for members of the group).
At the time I had worked almost three months as an IT security consultant at Defensify where I focus on performing security assessments of companies’ web applications. I was on vacation and decided to take a quick look at CSN’s PIN login functionality. Every user in their system has a four-digit PIN that is printed in various mails from them. It’s used to log in to one’s My Pages on the web and to use Interactive Voice Response (IVR) via phone. Being a former Swedish university student myself I have an account and also an active loan. It didn’t take long until I suspected a possibility to perform a brute-force attack on anybody’s account.
The Login by PIN Functionality
If you don’t want a detailed description of CSN’s login functionality, you can skip to the vulnerabilities.
The Log in to My pages page presents two PIN related options:
- Log in with personal code
- Order personal code

The login by PIN form looks as follows. One enters one’s personal identity number, which is like a social security number but public, as username.
![Log in with personal code from CSN Log in
with personal code from CSN --- Fill in your social security number and your
personal code. Forgot your personal code? Link: Order a new personal code. ---
Login with personal code [Help] --- Social security number (YYMMDDXXXX): [text
field: 830430XXXX] --- Personal code: [password field: ****] --- [Sign in]](../../../../images/csn_eng_login_pin.png)
If one enters the wrong PIN a warning is displayed saying the account will become locked after five attempts and one has to order a new PIN:
![Your personal code is incorrect Log in with
personal code from CSN --- Note --- Your personal code is incorrect. Make sure
you enter the correct code. After five incorrect attempts, you will be blocked
and will have to order a new personal code. --- Fill in your social security
number and your personal code. Forgot your personal code? Link: Order a new
personal code. --- Login with personal code [Help] --- Social security number
(YYMMDDXXXX): [text field: 830430XXXX] --- Personal code: [password field] ---
[Sign in]](../../../../images/csn_eng_wrong_pin.png)
And after five incorrect attempts the error message looks like this:
![Too many failed attempts Log in with
personal code from CSN --- Note --- You have made too many incorrect attempts
when filling in the personal code. Therefore, you must order a new code. ---
Fill in your social security number and your personal code. Forgot your
personal code? Link: Order a new personal code. --- Login with personal code
[Help] --- Social security number (YYMMDDXXXX): [text field: 830430XXXX] ---
Personal code: [password field] --- [Sign in]](../../../../images/csn_eng_too_many_tries.png)
So now the account is locked and all further PIN guesses will fail, but one can order a new PIN. Here is the form for that:
![Order personal code order personal code ---
Here you order a new personal code for My pages and Voicemail. You can get the
code via text message, email or letter. --- Remember to always keep your mobile
number and e-mail address current with us. In My profile you can see and change
your information. For security reasons, we can not change your contact
information by phone or e-mail. --- Do you know that you can use
e-identification instead of your personal code? --- New personal code ---
Personal: [text field: 830430-XXXX] --- > Link: I do not have a social
security number --- Write down the numbers --- Write down the numbers you see
in the picture. You must do this so that we can check that it is not a computer
that fills in the information automatically. --- Image Verification --- [image:
61384] Link: Can't see the picture? When you click on the link, the numbers
will appear in a new window. It takes about 30 seconds until you see the
numbers. --- Write the numbers you see in the picture: [text field: 61384] ---
[Next>]](../../../../images/csn_eng_order_personal_code.png)
As you can see one enters one’s personal identity number and solves a numeric CAPTCHA. The next step pictured below is to choose the delivery method for the new PIN. There are one to three options available depending on what contact information CSN has stored. Delivery by snail mail is always possible, and delivery by text message (SMS) and/or email is available if CSN has one’s mobile phone number and email address respectively.
![Choose the way you want us to send your code order personal code --- Choose the way you want us to send your code. ---
If we do not have your mobile number or email address, we can only send the
code by letter. Then it takes about three days until you get the code if you
live in Sweden. --- When you order a new personal code, your previous code
becomes invalid. If you try to log in with it after ordering a new code, the
new code may also become invalid. --- New personal code --- The code must be
sent with: --- Disabled option: () SMS --- Option: ( ) E-mail --- Option: ( )
mail --- [Order]](../../../../images/csn_eng_order_code_method.png)
After selecting the delivery method a confirmation page is displayed. Here is the confirmation after selecing email as method:

So, that’s the whole PIN login flow. At this moment the first time I tried the whole flow, I immediately suspected that there was a way to use brute force to login to anybody’s account – there’s less than 10,000 possible PINs after all (some are blocklisted, like 1234). I got that feeling based on a mistake I had previously seen in a customer engagement at Defensify. If you don’t already suspect something they might have done wrong, please pause here and think for a minute.
The Vulnerabilities
One of the vulnerabilities is that it’s possible to guess PINs unlimited number
of times. One has to reset the PIN (order a new one) after five attempts, but
there seems to be no limit in
the number of times that can be done. The fact that the PIN changes doesn’t
affect the probability to guess the correct one. Given that the new PIN is
assigned randomly and that one guesses randomly, the probability to guess the
PIN right is p ≈ 5 / 10000 ≈ 0.05% for every new PIN. On average (to have at
least a 50% chance) one needs to reset the PIN
times and make in total 1386 * 5 = 6930 login attempts before finding the
correct PIN.
One problem with performing that many login attempts and PIN resets is that it must be automated because almost nobody has the patience to do that manually. But a CAPTCHA must be solved every time one wants to reset the PIN. Or must it?
The other vulnerability is in the CAPTCHA requirement as that rhetorical question suggests. Step one described above is to fill in one’s personal identity number and to solve a CAPTCHA. The next and last step is to choose a delivery method for the PIN. Submitting that second form successfully generates a new PIN and sends it via the chosen media. The vulnerability lies in that the last step can be repeated any number of times without solving a new CAPTCHA. So an attacker just needs to solve one CAPTCHA per victim.
Here is the HTTP request that can be repeated indefinitely to generate new PINs:
POST /bas/ekund/personligKodAction.do HTTP/1.1
Host: tjanster.csn.se
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:79.0) Gecko/20100101 Firefox/79.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 39
Origin: https://tjanster.csn.se
Connection: close
Referer: https://tjanster.csn.se/bas/ekund/personligKodAction.do
Cookie: JSESSIONID=<REDACTED>; testcookie=true; WT_FPC=id=<REDACTED>:lv=<REDACTED>:ss=<REDACTED>; _ga=; _gid=GA<REDACTED>; valdgruppmenyCookie=personligt; _gat_UA-<REDACTED>=1; _gali=PersonligKodForm
Upgrade-Insecure-Requests: 1
send=E&metod%231*utskickssatt=Best%E4ll
HTTP response to the above request:
HTTP/1.1 302 Found
Date: Thu, 06 Aug 2020 12:16:34 GMT
Server: Apache
X-Powered-By: Servlet/3.0
Location: https://tjanster.csn.se/bas/ekund/personligKodAction.do?metod=redirect&forward=layout.gemensamma.texter&textId=tjanst.bestallapersonligKod.e-post
Content-Length: 0
Set-Cookie: ADRUM_BTa=R:55|g:<REDACTED>; Expires=Thu, 06-Aug-20 12:17:04 GMT; Path=/; Secure
Set-Cookie: ADRUM_BTa=R:55|g:<REDACTED>|n:customer1_<REDACTED>; Expires=Thu, 06-Aug-20 12:17:04 GMT; Path=/; Secure
Set-Cookie: SameSite=None; Expires=Thu, 06-Aug-20 12:17:04 GMT; Path=/; Secure
Set-Cookie: ADRUM_BT1=R:55|i:<REDACTED>; Expires=Thu, 06-Aug-20 12:17:04 GMT; Path=/; Secure
Set-Cookie: ADRUM_BT1=R:55|i:<REDACTED>|e:<REDACTED>; Expires=Thu, 06-Aug-20 12:17:04 GMT; Path=/; Secure
Set-Cookie: ADRUM_BT1=R:55|i:<REDACTED>|e:<REDACTED>|d:<REDACTED>; Expires=Thu, 06-Aug-20 12:17:04 GMT; Path=/; Secure
Expires: Thu, 01 Dec 1994 16:00:00 GMT
Cache-Control: no-cache="set-cookie, set-cookie2"
Connection: close
Content-Type: text/html
Content-Language: sv-SE
The idea was to write a simple Python script to automate the above attack method as a proof of concept, but I thought sending thousands of requests to CSN’s servers could be considered an attack of their infrastructure and be illegal so I decided to ask for permission to attack myself first. CSN never responded to that request, however, so no script is developed.
Reporting The Vulnerabilities
It was quite hard to find the right way to contact CSN to report the vulnerabilities. The best way on their website was a form for general questions, but I decided to make an Internet search instead to speed up the process of reaching somebody in charge of security. I searched for site:csn.se and information security responsible (but in Swedish). I got a few hits in published PDF documents where I could find a name. I didn’t know if the person was still the responsible person so I checked their LinkedIn profile where they stated that they is the information security responsible at CSN. I decided to send an email to the address on the format firstname.lastname@csn.se and also to security@csn.se. I got a bounce message saying that the security email address did not exist.
I quickly got in touch with a person who wants to stay anonymous who agreed on a way of encrypted communications. Either the information security responsible was on vacation and that person had access to their mailbox or the responsible silently forwarded my initial email. After I sent my report the anonymous person quickly acknowledged the reception of it.
After that nobody communicated with me for a long time and I never got permission to build a proof of concept script to verify that my attack scenario would work. After one and a half month the information security responsible, who was the recipient of my initial email to CSN, reached out thanking me for the report and told me that CSN were working on mitigations. I again asked for permission to do a proof of concept but never got a reply.
Recommendations to CSN
The following recommendations were sent to CSN along with the vulnerability descriptions the 6th of August 2020. Some complement each other and some are needed together.
- Restrict how many times or how often a new PIN code can be ordered. It seems unlikely that a user would need to order a new PIN more than once per day.
- Make it mandatory to solve a new CAPTCHA every time a new PIN code is ordered so that it becomes harder to automate attacks
- Investigate logs to see if others have found the same vulnerabilities and used them to login to anybody’s account. If so, inform affected users.
- Discuss with the DPO (Data Protection Officer) of CSN whether an incident report to the Swedish Data Protection Authority Datainspektionen is needed
- Define alerts in the monitoring systems to detect intrusion attempts like the ones described above
- Check whether an attacker can request hundreds or thousands of physical letters with new PIN codes sent to a victim - either as a joke or just to waste CSN’s money
- Add a possibility for users to disable login via PIN
- Automatically disable PIN logins when a user logs in via the electronic citizen solution BankID the next time (creds to group member Magnus Danielson for that suggestion)
- Publish a Vulnerability Disclosure Policy on www.csn.se to inform visitors how to get in touch with the right people in case of suspected security problems
- Publish a security.txt file on www.csn.se and tjanster.csn.se
Changes Made Since The Report
Friday the 13th of November 2020, while working on this blog post, I noticed that CSN had done some changes to the login by PIN functionality. They have moved the solving of the CAPTCHA to step two, which I suspect is done in response to my findings. That is not an improvement security wise however since it’s still possible to repeat the second request (including the solution to the CAPTCHA) many times. I successfully generated 20 emails with new PINs to myself within one minute.
The new flow for ordering a new PIN follows.
Step 1:
![Order personal code order personal code ---
Here you order a new personal code for My pages and Voicemail. You can get the
code via text message, email or letter. --- Remember to always keep your mobile
number and e-mail address current with us. In My profile you can see and change
your information. For security reasons, we can not change your contact
information by phone or e-mail. --- Do you know that you can use
e-identification instead of your personal code? --- New personal code ---
Social security number: [text field: 830430-XXXX] --- > Link: I do not have
a social security number --- [Next>]](../../../../images/csn_eng_order_personal_code_nov.png)
Step 2:
![Choose the way you want us to send your code order personal code --- Choose the way you want us to send your code. ---
If we do not have your mobile number or email address, we can only send the
code by letter. Then it takes about three days until you get the code if you
live in Sweden. --- When you order a new personal code, your previous code
becomes invalid. If you try to log in with it after ordering a new code, the
new code may also become invalid. --- New personal code --- The code must be
sent with: --- Disabled option: () SMS --- Option: ( ) E-mail --- Option: ( )
mail --- Write down the numbers --- Write down the numbers you see in the
picture. You must do this so that we can check that it is not a computer that
fills in the information automatically. --- Image verification --- [image:
13635] Link: Can't see the picture? When you click on the link, the numbers
will appear in a new window. It takes about 30 seconds until you see the
numbers. --- Write the numbers you see in the picture: [text field: 13635] ---
[Order]](../../../../images/csn_eng_order_code_method_nov.png)
Here is the new HTTP request that can be repeated indefinitely to generate new PINs:
POST /bas/ekund/personligKodAction.do HTTP/1.1
Host: tjanster.csn.se
Connection: close
Content-Length: 78
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: https://tjanster.csn.se
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.183 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: https://tjanster.csn.se/bas/ekund/personligKodAction.do
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Cookie: JSESSIONID=<REDACTED>; testcookie=true; _ga=GA<REDACTED>; _gid=GA<REDACTED>; valdgruppmenyCookie=personligt; WT_FPC=id=<REDACTED>:lv=<REDACTED>:ss=<REDACTED>
send=E&metod%231*robotsparr=&inmatadCaptcha=13635&metod%231*utskicksSatt=Order
HTTP response to the above request:
HTTP/1.1 302 Found
Date: Sun, 15 Nov 2020 22:21:17 GMT
Server: Apache
X-Powered-By: Servlet/3.0
Location: https://tjanster.csn.se/bas/ekund/personligKodAction.do?metod=redirect&forward=layout.gemensamma.texter&textId=tjanst.bestallapersonligKod.e-post
Content-Length: 0
Set-Cookie: ADRUM_BTa=R:55|g:<REDACTED; Expires=Sun, 15-Nov-20 22:21:47 GMT; Path=/; Secure
Set-Cookie: ADRUM_BTa=R:55|g:<REDACTED>|n:customer1_<REDACTED>; Expires=Sun, 15-Nov-20 22:21:47 GMT; Path=/; Secure
Set-Cookie: SameSite=None; Expires=Sun, 15-Nov-20 22:21:47 GMT; Path=/; Secure
Set-Cookie: ADRUM_BT1=R:55|i:<REDACTED>; Expires=Sun, 15-Nov-20 22:21:47 GMT; Path=/; Secure
Set-Cookie: ADRUM_BT1=R:55|i:<REDACTED>|e:<REDACTED>; Expires=Sun, 15-Nov-20 22:21:47 GMT; Path=/; Secure
Set-Cookie: ADRUM_BT1=R:55|i:<REDACTED>|e:<REDACTED>|d:<REDACTED>; Expires=Sun, 15-Nov-20 22:21:47 GMT; Path=/; Secure
Expires: Thu, 01 Dec 1994 16:00:00 GMT
Cache-Control: no-cache="set-cookie, set-cookie2"
Connection: close
Content-Type: text/html
Content-Language: sv-SE
Panic Shutdown
On the same day as, but prior to, the publication of this post CSN decide to take down the vulnerable order personal code page for maintenance. Either they took the decision entirely on their own after I notified them about my planned time for publication or the fact that I contacted the Swedish CERT during the day influenced their decision.

Conclusions
I recommend organizations to publish a security.txt file with a communication channel for vulnerability reports, accompanied with a vulnerabilty disclosure policy (VDP), and to cooperate with security researchers reporting vulnerabilities to avoid misunderstandings, confirm the vulnerability and let the researcher verify possible solutions.
Software Used
- Mozilla Firefox 79.0 (64-bit)
- Burp Suite Professional v2020.7
- Translations to English with Burp’s built-in Chromium 84.0.4147.89 (Official Build) (64-bit)
Comments?
Do you have questions, comments or corrections? Please interact with the tweet or LinkedIn post or make a pull request.
Credit
- Shadi Domat for bringing my attention to security questions around CSN’s login by PIN functionality
- Magnus Danielson for the suggestion to disable PIN logins when logging in with BankID, which I forwarded to CSN, and reviewing
- Calle Svensson and Alexander Kjäll for help with maths (the probabilities) and reviewing
- latex2png.com for rendering the formula
- Linus Kvarnhammar for reviewing
Follow-Up: Another CAPTCHA Problem Hidden In Plain Sight
See my next post CSN Follow-Up: Another CAPTCHA Problem Hidden In Plain Sight.