write-ups-challenges-2019-2020/pharo-challenge/writeup/README.md

162 lines
4.7 KiB
Markdown
Raw Normal View History

2022-11-24 21:43:03 +00:00
Looking in to the mirror
============================
## Introduction
This challenge was about employing reflection techniques to modify and view internal structures of the virtual machine.
Pharo is a Smalltalk environment, most widely known for its metaprogramming and reflection capabilities. Everything in Smalltalk is an object, and every object is an instance of a class, even a class itself is an instance of something (a metaclass).
The syntax of a Smalltalk program is relatively simple, the most important part is how to send messages (in other object oriented languages also called: invoking a method).
Lets say we want to send a message `newWithName` to a class `Person`, this can be achieved as follows:
```
p := Person newWithName:'Bram'.
```
Now the variable `p` contains a reference to an instance of the class `Person`.
Messages that can be send to classes themselves are defined on the metaclass of a class. In this case the metaclass is the `Person class` class. We can obtain a reference to a method itself by using the `>>` (lookup) operator:
```
(Person class)>>#newWithName
```
## The Challenge
The challenge consisted of connecting to a remote endpoint using netcat, which provided a Smalltalk REPL where Smalltalk expressions
could be executed.
The Pharo VM contained serveral user-defined classes:
* Challenge
* Squeak
The REPL suggested that you could obtain the flag by using:
```
Challenge new flag
```
however, when trying to do this, the REPL would yield that `only a Squeak could to that`, which means that the method `flag` could only be called from an instance of the class `Squeak`.
Luckily, the `Squeak` class contained a method named `fetchFlag`. So lets try to do this:
```
Squeak new fetchFlag.
```
This however, yields an initialisation error, suggesting that no instance of a `Squeak` could be created.
## Solution(s)
As Pharo has many ways to interact with classes and objects, the challenge has many solutions. Here, I will only present two possible solutions. One involves inspecting the method dictionnary of the `Challenge` class, the other involves overwriting the behaviour in the `Squeak` class that prevents its instance creation.
### Inspecting the Challenge class
Its is clear that we need to obtain some representation of the source of the `flag` method of the `Challenge` class.
We can try to obtain a reference to this method by using the lookup operator:
```
Challenge>>#flag
```
however, this results in a `LookupError`, the `Challenge` class seems to be too well protected.
However, Pharo has many ways to get references to methods, so we can try another one:
```
Challenge methodDict at:#flag
```
This seems to work well. Now we only need to print its source code.
```
(Challenge methodDict at:#flag) ast nodesDo: [:n | Transcript show: n; cr. ]
```
Which yields:
```
RBTemporaryNode(caller)
RBAssignmentNode(caller := thisContext client)
RBMessageNode(thisContext client)
RBThisContextNode(thisContext)
RBTemporaryNode(caller)
RBMessagifTrue: [ ^ 'IG{ImSoMetaEvenThisAcronym}' ])
RBMessageNode(caller class = Squeak)
RBMessageNode(caller class)
RBTemporaryNode(caller)
RBGlobalNode(Squeak)
RBBlockNode([ ^ 'IG{ImSoMetaEvenThisAcronym}' ])
RBSequenceNode(^ 'IG{ImSoMetaEvenThisAcronym}')
RBReturnNode(^ 'IG{ImSoMetaEvenThisAcronym}')
RBLiteralValueNode('IG{ImSoMetaEvenThisAcronym}')
RBReturnNode(^ 'Only a Squeak can do that (see Squeak>>#fetchFlag)')
RBLiteralValueNode('Only a Squeak can do that (see Squeak>>#fetchFlag)')
```
And there is our flag.
### Recompiling the Squeak class
Another way to solve this challenge is to allow the `Squeak` class to be initialised.
At first, we are not entirely sure why the `Squeak` class fails to initialise.
It could be that its `initialize` method has been overridden.
We can find out which methods have been overriden by retrieving the methods of the `Squeak` class.
```
Squeak methods
```
which only yields `Squeak>>#fetchFlag`. We can conclude that the `InitialisationError` is signaled elsewhere.
Maybe it is at the class side?
```
(Squeak class) methods
```
which yields `(Squeak class)>>#basicNew`.
Obtaining a reference to this method using `(Squeak class)>>#basicNew` yields:
```
basicNew InitialisationError signal.
```
Hence, we found the culprit. We can disable this behaviour by recompiling that method to something that just
delegates the `basicNew` message to its parent:
```
(Squeak class) compile: 'basicNew ^ super basicNew'
```
Now, we can try to get an instance of our `Squeak` again.
```
Squeak new.
```
Which, indeed, yields `a Squeak`, mission accomplished.
The last part of this solution consisted of calling the `fetchFlag` method on that instance:
```
Squeak new fetchFlag.
```
Which yields:
```
IG{ImSoMetaEvenThisAcronym}
Challenge
```
Success!