⚠️⚠️⚠️

Penpa+ has disabled external scripts so these tricks no longer work on the default Penpa+. I now host my own penpa fork, dangerous-penpa-edit that will not be maintained but allows external scripts. Links on this page will send you to my fork, dangerous-penpa-edit.


Penpa+ is a web application meant to create paper-and-pencils puzzles. It’s known for being more customizable than other puzzle-setting web apps such as puzz.link as you are not limited to creating puzzles in certain genres.

With all this freedom, I was recently introduced to the fact that when writing the description/rules of the puzzle, Penpa+ does not sanitize the content before inserting it into the DOM. This means that the site can be prone to an XSS attack, allowing people to inject external scripts into what seems to be a trusted application. This of course can be used for harm, but I decided to have a little fun with it.

2x2 Masyu

Snake

How do you inject a script into Penpa?

With the lack of sanitation, it’s quite simple; insert this line of code into the title (or any other) field:

<img src=x style='display:none' onerror='alert("hi")'

Penpa+ attempts to find the file at /x and fails to do so, so it runs the JS code in the onerror event. You can replace the alert("hi") with whatever code you want.

Note that if you put this in the rules field, it’ll trigger when you first load the page, and then every time you “show rules”.

How did you create Snake?

I don’t fully understand the source code of Penpa+, as I read just enough to understand what I needed to make Snake. Essentially, there is a pu object which represents the grid. We read and modify its attributes pu_a and pu_q which represent the elements (like surface shading, edges, etc.) of the answer (player input) and question (what is set before) respectively. I stuck some simple snake code to modify these attributes in a setInterval() function, then repeatedly calling pu.redraw() gives us animation. There are a lot of details like managing the indices / setting colors or style and whatnot that you’ll have to look into deeper if you want to create anything fancy.

The minimized code is all available if you inspect the source code of the title. The specific snake code is nothing special, just something I threw together for the sake of demonstration. There is theoretically an end, but I did not bother to check if my win detection works (though no answer checking unfortunately).