IIS 7.0 : Runtime Web Server Extensibility (part 6) - Creating and Managing Handler Mappings

- How To Install Windows Server 2012 On VirtualBox
- How To Bypass Torrent Connection Blocking By Your ISP
- How To Install Actual Facebook App On Kindle Fire
6/14/2012 4:53:27 PM

Creating and Managing Handler Mappings

Though Appcmd provides a top-level Module object for managing modules, it does not provide a top-level view of the handler mappings configuration. However, you can always use the CONFIG object to directly manage the system.webServer/handlers configuration section to accomplish any needed handler mapping management task.

Discussing the full collection editing syntax of the CONFIG object is out of scope for this section , but here are some examples for using it to do basic handler mapping management tasks.

Adding a Handler Mapping

To add a handler mapping to a native module, run the following command on one line.

AppCmd set config /section:handlers "/+[name='TestHandler',path='*test',


Long commands are sometimes shown formatted on multiple lines to fit on the printed page.

This adds a server-level handler mapping that maps GET, POST, and HEAD requests to *.test URLs to the "TestModule" native module. Though only the name, path, and verb attributes are required (and the modules attribute should be set for the native module handler mapping), you can also specify any of the other attributes that apply. You can also specify the configuration path at which this mapping should be added instead of adding it at the server level, as shown in the next example.

To add a handler mapping to a managed handler type, run the following command on one line.

AppCmd set config "Default Web Site/" /section:handlers

This adds a handler mapping for the root of the "Default Web Site" Web site, which maps all requests to *.aspn URLs to the .NET MyHandlers.ASPNHandler handler type. Notice that the handler mapping is also preconditioned to be used only in application pools running in Integrated mode. This is required for handler mappings to managed types, because only Integrated mode supports adding managed handler types directly into IIS handler mappings.

Editing a Handler Mapping

To edit a handler mapping, use the following code.

AppCmd set config /section:handlers /[name='TestHandler'].verb:GET

This sets the verb attribute of the handler mapping identified by the name TestHandler to the new value of GET. Note that the name attribute serves as the unique collection key for the handlers configuration section.

You can use this syntax to edit any of the handler mapping attributes. You can also edit the handler mapping you created at the Default Web Site/ level by specifying that path after the SET CONFIG command.

Deleting a Handler Mapping

To delete a handler mapping, you can use the /- prefix for deleting configuration collection elements.

AppCmd set config /section:handlers /-[name='TestHandler']

This deletes the handler mapping you created earlier, identified by the name TestHandler. You can also delete the handler mapping you created at the "Default Web Site/" level by specifying that path after the SET CONFIG command.

Adding Entries to the ISAPI CGI Restriction List (Formerly Web Service Restriction List)

When creating handler mappings that use CgiModule or IsapiModule to support CGI programs or ISAPI extensions respectively, you also need to allow the specific CGI program or ISAPI extension by adding it to the ISAPI CGI Restriction List. In IIS 6.0, this is known as the Web Service Restriction List and is a key security measure to control the execution of third-party code on the server.

For example, to add an ISAPI extension DLL to the system.webServer/security/isapiCgiRestriction list, use the following command.

appcmd set config /section:isapiCgiRestriction /+[path='c:\myisapi.dll',

To allow (or disallow) an existing entry in the list, use the following command.

appcmd set config /section:isapiCgiRestriction

To delete an entry in the list, use the following command.

appcmd set config /section:isapiCgiRestriction /-[path='c:\myisapi.dll']

You can specify both CGI programs (executables) and ISAPI extensions (DLLs) in this list.


FastCGI program executables are not added to the isapiCgiRestriction list. Instead, they must be registered in the system.webServer/fastCGI configuration section to allow FastCGI application pools to be created for this executable.

Securing Web Server Modules

The extensibility architecture in IIS 7.0 is in many ways the recognition of the fact that it’s not really the server but rather the application that runs on it that makes all the difference. Unfortunately, history shows that it is also the application that is most commonly the cause of security vulnerabilities. The lockdown approach to security that IIS 6.0 provides—restricting the ability to run new code on the server and reducing the privilege of that code—has been immensely successful in reducing Web server exploits. Now, with IIS 7.0, server administrators must strike a balance between the functionality afforded by the new extensibility model and server security. Therefore, it is now more important than ever to understand the security impact of hosting server extensibility and to know how to properly lock it down to avoid weakening the security of your server.

When it comes to securing the server, one of the biggest problems administrators face today is dealing with system complexity and being able to properly apply key security best practices rather than being bogged down in the details. This approach, though not a replacement for proper security threat modeling and penetration testing at the application level, enables you to significantly reduce the general security risk to the server. Basically, you need to be able to answer the following question: Assuming you cannot trust the code running on your server to be completely foolproof, how can you control what it can and cannot do, and at the same time prevent your server from being compromised if this code is exploited?

The best answer to this question is to approach it in the following stages. First, you need to know how to reduce the server’s surface area to a minimum, removing all functionality that is not essential to its operation. Second, you need to understand the privilege of code that executes on the server and then reduce it as much as possible. Finally, you need to maintain control over the extensibility that is allowed on the server, preventing undesired functionality from being added or desired functionality from being removed. 

Taking Advantage of Componentization to Reduce the Security Surface Area of the Server

To completely secure a Web server, you would need to disconnect it from the network, unplug it, and bury it in a thick slab of concrete. This would guarantee its security by removing the possibility of any malicious interactions with the system. However, because this will also make the Web server useless, you have to find other ways to apply the security principle of reducing the attack surface area.

The ability of IIS 7.0 to remove virtually all features of the Web server is fundamental here, enabling us to eliminate the threat of any known or unknown attack vectors that may exist in those features. In addition, removing extra functionality reduces management complexity and reduces the chance of your server being forced offline if a patch affects the removed features. You can leverage this ability by doing the following:
  1. Determine the set of features that you need for your server/application.

  2. Install only the required IIS 7.0 features by using Windows Setup.

  3. Manually remove any modules that your application does not need.

At the end of step 2, you should have the minimum required set of functionality installed globally for your server. You can then further reduce the surface area by disabling the modules you do not need in each of your applications, in the case where each application requires slightly different server functionality.

In some other cases, you may need to disable modules for the entire server if the setup packaging is not granular enough. This is often the case with ASP.NET, which installs all of the ASP.NET modules and handlers whether or not they are all necessary in your application.

Sounds simple, right? Unfortunately, the biggest challenge lies in step 1, determining the set of features your application needs. Doing this requires knowing which functionality can be safely removed without negatively affecting your application’s functionality or compromising its security. That’s right—you can actually end up making your server a lot less secure if you remove a security-sensitive feature. For example, if you remove an authorization module that is responsible for validating access to an administration page in your application, you may end up allowing anonymous users to take administrative action on your server! Likewise, removing certain features may contribute to decreased performance or reduced stability. Or removing these features may simply break your application.


It is possible to have IIS configuration that configures an IIS feature to perform a certain function and yet to not have this function be performed by the server if the module that is responsible for this function is not enabled. This can happen if someone disables the module or configures its preconditions to prevent it from running in a specific application pool. Because of this, you need to make sure that the required modules are present and are correctly preconditioned to insure the correct operation of your application.

Therefore, it is important to understand what features your application requires, and which it does not. To help with this, you can consult Table 9, which illustrates the function played by each built-in IIS 7.0 module whose removal may have a security impact on the server.

Table 9. Function of Built-In Modules with Security Implications


Purpose and Removal Effect

Anonymous Authentication Module

Purpose: Authenticates the request with an anonymous user if no other authentication mechanism is present.

If removed: Access to resources will be denied for anonymous requests.

Basic Authentication Module

Purpose: Supports basic authentication.

If removed: Clients will not be able to authenticate with basic authentication.

Certificate Mapping Authentication Module

Purpose: Supports client certificate authentication.

If removed: Clients will not be able to authenticate with client certificates.

Configuration Validation Module

Purpose: Validates ASP.NET configuration in integrated mode

If removed: Strong warning—ASP.NET applications that define modules and handlers using legacy configuration will silently run in integrated mode, but the modules will not be loaded. This may result in unexpected application behavior and security vulnerabilities for unmigrated ASP.NET applications.

CustomError Module

Purpose: Detailed error messages will not be generated for IIS errors.

If removed: Strong warning—Sensitive application error information may be sent to remote clients.

Default Authentication

Purpose: Supports the ASP.NET DefaultAuthentication_OnAuthenticate event.

When ASP.NET is configured to use the Forms Authentication mode, removing this module may lead to errors in other ASP.NET modules during anonymous requests.

If removed: Warning—The DefaultAuthentication_OnAuthenticate event will not be raised, so any custom authentication code depending on this event will not run. This is not common.

Digest Authentication Module

Purpose: Supports digest authentication.

If removed: Clients will not be able to use digest authentication to authenticate.

File Authorization

Purpose: Verifies that the authenticated client has access to physical resources.

If removed: Strong warning—Access may be granted to resources that deny access to the authenticated user.

Forms Authentication

Purpose: Supports forms-based authentication.

If removed: Clients will not be able to use forms authentication to authenticate.

HttpCache Module

Purpose: Supports IIS output caching and kernel caching of responses.

If removed: Warning—Response output caching will not occur, possibly resulting in increased load on the server and in the worst case Denial of Service (DoS) conditions.

HttpLogging Module

Purpose: Supports request logging.

If removed: Warning—Requests may not be logged.

IISCertificate Mapping Authentication Module

Purpose: Supports IIS configuration–based client certificate authentication.

If removed: Clients may not be able to authenticate with client certificates against IIS configuration.

HttpRedirection Module

Purpose: Supports configuration-based redirection rules.

If removed: Warning—If the application depends on redirection rules for restricting access to content, removing this module may make otherwise protected resources available.

IsapiFilter Module

Purpose: Supports ISAPI filters.

If removed: Strong warning—ISAPI filters that enforce access or have other security functionality will not run.


Purpose: Supports ASP.NET response output caching.

If removed: Warning—ASP.NET response output caching will not occur, possibly resulting in increased load on the server and in the worst case Denial of Service (DoS) conditions.

RequestFiltering Module

Purpose: Enforces various request restrictions and protects hidden content.

If removed: Strong warning—Removing this module may result in protected content being served. It may also lead to nonspecific security vulnerabilities resulting from allowing unrestricted request input into the application.


Purpose: Supports the ASP.NET roles functionality.

If removed: Strong warning—Roles for the authenticated user may not be available, which may cause authorization decisions to be affected. Typically, this will only restrict access, but in some cases where access is denied based on roles, this may grant access to otherwise unauthorized users.

Static Compression Module

Purpose: Supports compression of static resources.

If removed: Warning—Removing this module may result in significantly higher bandwidth for the site, because compression of static resources will be disabled.

Url Authorization

Purpose: Supports declarative access rules.

If removed: Strong warning—URL authorization access rules will be ignored, and access may be granted to unauthorized users.

Url Authorization Module

Purpose: Supports declarative ASP.NET access rules.

If removed: Strong warning—ASP.NET url authorization access rules will be ignored, and access may be granted to unauthorized users.

Windows Authentication

Purpose: Supports NTLM and Kerberos authentication.

If removed: Clients will be unable to authenticate with NTLM or Kerberos Windows authentication.

Windows Authentication Module

Purpose: Supports raising the ASP.NET WindowsAuthentication_OnAuthentication event.

If removed: Warning—WindowsAuthentication_OnAuthentication event will not be raised, so any custom ASP.NET authentication code dependent on this event will not run. Note that this module is not required for Windows authentication.

You should always verify that the application does indeed have all of the required modules enabled after deployment. In addition, you should always test the application whenever the module configuration changes to insure its correct operation with the new module set. Armed with the knowledge of which modules can be safely removed, you can take advantage of IIS 7.0’s modularity to significantly reduce the surface area of your server, without accidentally reducing its security.

Understanding and Reducing the Privilege of Code that Runs on Your Server

Now that you have reduced the surface area of your server to the acceptable minimum, you need to secure the remaining functionality. This is typically done in two ways: by restricting the inputs to the application as much as possible by using security features such as authorization and request filtering (IIS 7.0’s version of UrlScan) and by reducing the privilege with which the code in the application executes so that even if it is compromised, it is limited in the amount of harm it can do. 

The former approach is an extension of reducing the surface area approach you took earlier, attempting to block as many of the attack vectors as possible by constraining the input to the server. The latter approach uses the principle of least privilege and focuses on what happens if the functionality on the server is compromised. With an understanding of how the extensibility code executes in the context of IIS, you can do much to reduce its privilege, which often makes compromising the server a lot harder or impossible. Also, this knowledge helps you understand the trust requirements for adding features or application components to the server.

Table 10 illustrates the privilege with which IIS Web server modules execute on the server.

Table 10. Module Privileges


Execution Scope

Privilege Level

Who Can Add

Native modules

IIS worker process

Application pool identity


Managed modules and handlers

ASP.NET appdomain

Application pool identity (default)


Authenticated user


Limited by ASP.NET trust level

Application owner

ISAPI filters

IIS worker process

Application pool identity


ISAPI extensions

IIS worker process

Authenticated user (default)


Application pool identity


CGI programs

CGI program process (single-request)

Authenticated user (default)


Application pool identity


FastCGI programs

FastCGI program process

Application pool identity


The majority of IIS extensibility is hosted within a long-running IIS worker process (everything except CGI and FastCGI programs that execute out of process), which executes under the configured application pool identity (Network Service by default). This includes native modules as well as ISAPI extensions and filters (managed modules and handlers are also included, but they provide an additional constrained execution model that is discussed later in this chapter). This code, therefore, can do everything that the application pool identity is allowed to do, based on the privileges granted to the identity by the system and rights granted by ACLs on Windows resources. Reducing the privilege of this identity from Local System in IIS 5.1 to Network Service in IIS 6.0 was one of the fundamental security improvements that enabled IIS 6.0 to achieve its stellar security record. 

Remember that despite all other constraining measures that may be in place, including ASP.NET Code Access Security, the privileges and rights granted to worker process that contains the code define what code in the process may or may not do (when impersonating, you also need to consider the rights and privileges assigned to the impersonated identity). In other words, when you add code to the server, even if it is application code, assume that it can do everything that your worker process is allowed to do. By maintaining least privilege application pools, you can significantly reduce the damage to the server in the case of an application compromise.

The story is slightly different when it comes to managed (ASP.NET) module and handler components. These components are hosted within the ASP.NET application, and in addition to being limited by the IIS worker process, they are also limited by the .NET Code Access Security (CAS) policy configured for the ASP.NET appdomain. This means that managed modules and handlers can execute with a lower privilege than the one granted by the IIS worker process identity.

By default, ASP.NET applications are configured to execute with Full trust, which means that they are not additionally constrained. By configuring the application to execute with lower trust levels via the system.web/trust configuration section, you can create an additional limitation on the execution of .NET code that precludes managed modules from performing certain actions and accessing resources outside of those located in their subdirectories. 


You can also use IIS Manager to configure the default trust level for ASP.NET applications or the trust level for a particular application on your server.

The recommended trust level is Medium. At this trust level, the application cannot access resources that do not belong to it, though it can still use most ASP.NET features and execute code that affects its own operation. At this trust level, multiple applications running within a single application pool are largely isolated from each other, making this level the correct one to use for shared hosting (although it is preferable to host each customer in a separate fully isolated application pool), where hosted applications are allowed to upload code to the server.

You should take advantage of the Medium trust level where possible to further reduce the privilege of the managed components of your application. Be aware that some ASP.NET applications or modules may not function in Medium trust, due to the use of .NET APIs that required a higher trust level. The number of these applications is getting smaller, due to both API improvements in .NET Framework 2.0+ and application improvements to facilitate operation in partial trust environments. Also, the ASP.NET run time may experience reduced performance in a partial trust. This needs to be evaluated in each specific case to determine whether it is a significant enough factor to warrant using higher trust levels.


Though ASP.NET trust levels provide an additional way to constrained the execution of managed code on your server, they should not be used as a substitute for reducing the privilege of the hosting IIS worker process.

CGI and FastCGI programs are not hosted inside the IIS worker process, but instead execute inside their own processes that are spawned by the IIS worker process. CGI programs by default execute under the identity of the authenticated user, although you can configure them to execute with the identity of the worker process. Therefore, when using CGI programs, be aware that the code has the privilege of the invoking user. If that user is an administrator on the server, the code can perform administrative tasks that can lead to complete server compromise if the code is successfully exploited.


When using anonymous authentication while impersonating, the new IIS_IUSR account will be used to execute the CGI, FastCGI, and ISAPI extension–based applications (unless the server is configured to use the process identity instead). Unless you are using the IIS_IUSR account to use lower privilege for the executing code than the one provided by the worker process identity, you should consider using the worker process identity instead to manage one less account. You can do this by setting the userName attribute of the anonymousAuthentication section to "".

FastCGI programs always execute with the identity of the IIS worker process, so they have the same privilege as code in the IIS worker process. FastCGI does provide a way for the FastCGI program to impersonate the authenticated user, which can be done by PHP in FastCGI mode. In this case, the same warning applies to code running in the worker process as when running CGI programs.


In a number of cases, server code impersonates the authenticated user. This is done by default for all ISAPI extensions, ASP pages, PHP pages running in CGI, ISAPI or FastCGI mode, and ASP.NET applications that enable impersonation. If impersonation is used, be aware that the code will execute with very high privileges when the request is made by a user who has administrative privileges on this machine. You should strongly consider disallowing administrative users from using your application except when necessary.

All that said, the bottom line is that you must trust the source of the code running on your server as far as the privilege level under which this code executes. If you do not trust the code, you must insure that it runs with a low privilege level by constraining it with a very low privilege application pool identity. If the code is native, that is the best you can do. If the code is managed, you can use ASP.NET’s partial trust levels to further constrain it, which provides a foundation for allowing third-party application code to run on your server.

If you do trust the code, you can harden it against unforeseen exploits by reducing its privilege as much as possible using the techniques described earlier in this chapter. Though you can never be completely sure that a piece of code is foolproof against attacks, using the least privilege principle can significantly limit or eliminate damages.

Locking Down Extensibility

Now that you have built a minimal surface area Web server that runs with least privilege, you need to make sure it stays that way. If you are running a dedicated server, this is less of an issue because you are the one who controls the configuration that defines which components are enabled and how they execute. However, if you delegate application management to someone else, as is the case with shared hosting servers and sometimes departmental servers, things are different.

To understand this, let’s first look at the difference in extensibility delegation between IIS 6.0 and IIS 7.0. In IIS 6.0, the administrator in the metabase controls the server functionality. If a user wants to serve .php3 pages with the PHP scripting engine, they need to contact the administrator to create a handler mapping for their application. The same is the case for adding or removing an ISAPI filter. In IIS 7.0, the delegated configuration system enables applications to remove or add new functionality in some cases, without requiring administrator level changes. This is nice for allowing xcopy deployment of applications that define their own configuration and functionality, and reducing total cost of ownership. However, in some cases, this may be undesired from a security perspective, and so the administrator has a fine degree of control over what changes are allowed at the application level. This is done by controlling configuration delegation for the system.webServer/handlers and system.webServer/modules configuration sections via configuration locking.

In the default IIS 7.0 installation, both of these sections are locked at the server level. This means that application A cannot add new modules or create new handler mappings, and application B cannot remove or change existing modules or handler mappings.

This is a very restrictive state that prevents many ASP.NET applications from working correctly, because ASP.NET applications often need to declare new modules and create new handler mappings. In general, it prevents IIS applications from defining their handler mappings and modules in their configuration. Because of this, when the ".NET Extensibility" or the "ASP.NET" role service is installed on the server, these sections are unlocked. This allows applications to specify a configuration that does the following:

  1. Enable/add new managed modules.

  2. Disable/remove existing modules.

  3. Add new handler mappings.

  4. Override/remove existing handler mappings.

Because adding new native modules requires installing them at the server level, and adding new ISAPI filters/extensions and CGI/FastCGI programs also requires configuration changes at the server level (the isapiCgiRestrictions and fastCgi sections), applications cannot introduce new native code. However, they can introduce new managed modules and handlers. Because of this, in shared environments where the application is not trusted by the administrator, server administrators must do one of the following:

  1. Prevent new managed modules/handlers from being added by locking the modules and handlers sections. This will break many applications (especially ASP.NET applications running in Integrated mode).

  2. Reduce the trust level of the application to Medium trust to constrain the execution of managed code.

  3. Use a low-privilege application pool to host the application.

The application can also by default disable any of the modules defined at the server level. This can be a problem if the server administrator wants to mandate the presence of a particular module, such as a bandwidth monitor module or logging module that tracks the operation of the application. To counteract that, the server administrator can lock each module that should not be removable at the server level, preventing it from being removed by the application. This can be done by adding a lockItem = "true" attribute to each module element in the modules configuration section at the server level. In fact, when the modules section is unlocked, ASP.NET setup automatically locks each native module that is installed against removal (in some cases, you may want to unlock some of these modules if you do not mind them being disabled by the application).

Because the application can also create new handler mappings, the application can override mappings defined at the server level. The locking approach does not work here because new mappings take precedence over earlier mappings, so it is not necessary to remove existing mappings to redirect them to other handler types. However, the ability to remap requests in the application to another handler is not a security concern outside of the ability to execute the code, which is already controlled by the application trust level and/or the application pool identity. The handlers section does expose the accessPolicy attribute, which controls what access levels are granted to the handlers. This attribute is by default locked at the server level, so the application cannot modify it.

The trust level configuration for the application is also something that should be locked at the server level. By default, it isn’t—so the application can elevate its own trust level to Full to remove the constraint on the execution of the .NET code. Server administrators should always lock this configuration in the framework’s root Web.config file to prevent it from being overridden when they are relying on the partial trust security model for applications. For convenience, you can do this using the following Appcmd command.

AppCmd lock config /section:trust /commit:MACHINE/WEBROOT

This prevents applications from overriding the trust level setting in the framework root Web.config.

  •  Friends Reunited ...Reunited?
  •  Java EE 6 : Servlet Development and Deployment - Request forwarding and response redirection
  •  Java EE 6 : Servlet Development and Deployment - Processing HTML forms
  •  Separating BPM and SOA Processes : BPM-Oriented Disputes with TIBCO (part 2) - BusinessWorks Orchestration Processes & ActiveMatrix ESB Processes
  •  Separating BPM and SOA Processes : BPM-Oriented Disputes with TIBCO (part 1) - Architecture & iProcess Business Processes
  •  Separating BPM and SOA Processes : Disputes on the Model Stack
  •  Discover the @-SIGN : From Wine Fairs to Email Addresses
  •  IIS 7.0 : Techniques for Enabling Application Frameworks (part 2) - Deploying Frameworks That Use FastCGI
  •  IIS 7.0 : Techniques for Enabling Application Frameworks (part 1) - Enabling New Static File Extensions to Be Served & Deploying Frameworks Based on IIS 7.0 Native Modules
  •  Better Back Up : Mozy, Carbonite, Dropbox, Jungledisk & Livedrive
    Top 10
    - Microsoft Visio 2013 : Adding Structure to Your Diagrams - Finding containers and lists in Visio (part 2) - Wireframes,Legends
    - Microsoft Visio 2013 : Adding Structure to Your Diagrams - Finding containers and lists in Visio (part 1) - Swimlanes
    - Microsoft Visio 2013 : Adding Structure to Your Diagrams - Formatting and sizing lists
    - Microsoft Visio 2013 : Adding Structure to Your Diagrams - Adding shapes to lists
    - Microsoft Visio 2013 : Adding Structure to Your Diagrams - Sizing containers
    - Microsoft Access 2010 : Control Properties and Why to Use Them (part 3) - The Other Properties of a Control
    - Microsoft Access 2010 : Control Properties and Why to Use Them (part 2) - The Data Properties of a Control
    - Microsoft Access 2010 : Control Properties and Why to Use Them (part 1) - The Format Properties of a Control
    - Microsoft Access 2010 : Form Properties and Why Should You Use Them - Working with the Properties Window
    - Microsoft Visio 2013 : Using the Organization Chart Wizard with new data
    Video Sports
    - The Banner Saga 2 [PS4/XOne/PC] PC Launch Trailer
    - Welkin Road [PC] Early Access Trailer
    - 7th Dragon III Code: VFD [3DS] Character Creation Trailer
    - Human: Fall Flat [PS4/XOne/PC] Coming Soon Trailer
    - Battlefleet Gothic: Armada [PC] Eldar Trailer
    - Neon Chrome [PS4/XOne/PC] PC Release Date Trailer
    - Rocketbirds 2: Evolution [Vita/PS4] Launch Trailer
    - Battleborn [PS4/XOne/PC] 12 Min Gameplay Trailer
    - 7 Days to Die [PS4/XOne/PC] Console Trailer
    - Total War: Warhammer [PC] The Empire vs Chaos Warriors Gameplay Trailer
    - Umbrella Corps [PS4/PC] Mercenary Customization Trailer
    - Niten [PC] Debut Trailer
    - Stellaris [PC] Aiming for the Stars - Dev. Diary Trailer #1
    - LawBreakers [PC] Dev Diary #4: Concept Art Evolutions
    programming4us programming4us