Flare-On 2024: bloke2
bloke2 involves reversing a Verilog description language project to find a hidden flag inserted by a missing developer. I’ll find a relatively long string of data and where an XOR might be applying it to the input test data, except it’s always disabled by a flag. I’ll enable that flag and the flag comes out while running the tests.
Challenge
The challenge prompt reads:
You’ve been so helpful lately, and that was very good work you did. Yes, I’m going to put it right here, on the refrigerator, very good job indeed. You’re the perfect person to help me with another issue that come up. One of our lab researchers has mysteriously disappeared. He was working on the prototype for a hashing IP block that worked very much like, but not identically to, the common Blake2 hash family. Last we heard from him, he was working on the testbenches for the unit. One of his labmates swears she knew of a secret message that could be extracted with the testbenches, but she couldn’t quite recall how to trigger it. Maybe you could help?
The download contains a bunch of .v
files along with a Makefile
and a README.md
:
$ ls
Makefile bloke2.7z bloke2b.v bloke2s.v data_mgr.v f_sched_tb.v g.v g_unit.v
README.md bloke2.v bloke2b_tb.v bloke2s_tb.v f_sched.v f_unit.v g_over_2.v
The README.md
file gives more background on the challenge:
bloke2
======
One of our lab researchers has mysteriously disappeared. He was working on the
prototype for a hashing IP block that worked very much like, but not identically
to, the common Blake2 hash family. Last we heard from him, he was working on
the testbenches for the unit. One of his labmates swears she knew of a secret
message that could be extracted with the testbenches, but she couldn't quite
recall how to trigger it. Maybe you could help?
Details
_______
This source code is written in the Verilog hardware description language. It is
written in the subset of SystemVerilog supported by the free and easily
available Icarus Verilog simulator (available under various package managers as
either `iverilog` or `icarus-verilog`). It is not tested on, nor is it
guaranteed to run on, any other Verilog simulators (and in fact, I would not
recommend trying, as Icarus is... somewhat more permissive in some aspects than
a lot of commercial tooling).
The files can be built via GNU Make with the accompanying `Makefile`; simply
`make` to build the total source and check for errors, or `make tests` to build
and run the testbenches. If you don't have GNU Make, you should be able to
build it with `iverilog -g2012 -s <top_module> *.v` followed by `vvp a.out` to
run the testbench. For example:
```
iverilog -g2012 -s f_sched_tb *.v
vvp a.out
```
You should be able to get to the answer by modifying testbenches alone, though
there are some helpful diagnostics inside some of the code files which you coulduncomment if you want a look at what's going on inside. Brute-forcing won'treally help you here; some things have been changed from the true implementation
of Blake2 to discourage brute-force attempts.
Happy hardware hacking!
- Dave
The key points are that I can get the flag by modifying the testbenches. There are diagnostics that are in the main code that I can uncomment.
Run It
Structure
Verilog is a hardware description language used to design and model electrical circuits. I’d never interacted with Verilog before this challenge, so I had to do a good bit of fumbling around.
The given files are a Verilog description language project, where bloke2.v
is the main file, and it imports data_mgr.v
and f_sched.v
.
The _tb
are the testbench files.
Setup
I’ll install verilog
(sudo apt install iverilog
), and then try running it with the Makefile
as the README.md
suggests:
oxdf@hacky$ make
iverilog -g2012 -o bloke2.out bloke2.v f_sched.v f_unit.v g_over_2.v g.v g_unit.v data_mgr.v bloke2s.v bloke2b.v
oxdf@hacky$ make tests
iverilog -g2012 -o f_sched.test.out f_sched.v f_sched_tb.v
vvp f_sched.test.out
iverilog -g2012 -o bloke2b.test.out bloke2.v f_sched.v f_unit.v g_over_2.v g.v g_unit.v data_mgr.v bloke2s.v bloke2b.v bloke2b_tb.v
vvp bloke2b.test.out
Received message: 7ց �A�&�377��S��3��
���2���&E�}'<���Y|N��:'�ԟ��,ً�� �6�A��C6
Received message: �q]y8��B��y��#>[��qi܊wRg��:���0���␦�$E���w�5CU��M-���
Received message: 0��+(s3����ۈ��'pɣ�$�vg�x�Ӝ�C�_�je������0��3ym�&�hA����Q
iverilog -g2012 -o bloke2s.test.out bloke2.v f_sched.v f_unit.v g_over_2.v g.v g_unit.v data_mgr.v bloke2s.v bloke2b.v bloke2s_tb.v
vvp bloke2s.test.out
Received message: ��F3��p���٨�p�{3xM�%���=W���
Received message: n��(� � �F�r�c��@lu�s�fFvr
Received message:
��a$�a�b��}���\�H��O?w�`?εb
rm f_sched.test.out bloke2s.test.out bloke2b.test.out
The test files output some garbled non-ASCII, but this is probably where the flag will come.
RE
note: I very much fumbled my way through this without fully understanding exactly what I was looking at or doing, so this isn’t the most comprehensive writeup.
data_mgr.v
This file in imported by bloke2.v
and contains some interesting stuff. Right away I’ll notice a long line of data in the middle of the file:
localparam TEST_VAL = 512'h3c9cf0addf2e45ef548b011f736cc99144bdfee0d69df4090c8a39c520e18ec3bdc1277aad1706f756affca41178dac066e4beb8ab7dd2d1402c4d624aaabe40;
That is interesting because it’s 512 bits of hex, which would be a good place to put a flag. A bit later, it’s used in a loop:
always @(posedge clk) begin
if (rst) begin
out_cnt <= 0;
end else begin
//$display("%t dmgr dout oc %h", $time, out_cnt);
if (h_rdy) begin
//$display("%t dmgr dout h %h t %b", $time, h_in, tst);
out_cnt <= W;
h <= h_in ^ (TEST_VAL & {(W*16){tst}});
end else if(out_cnt != 0) begin
//$display("%t dmgr dout d %h dv %b de %b oc %h", $time, data_out, dv_out, data_end, out_cnt);
out_cnt <= out_cnt - 1;
h <= {8'b0, h[W*8-1:8]};
end
end
end
What’s interesting is that it is taking the bit value of tst
and multiplying it by 512 (W
is 32). So if tst
is positive, then the h_in
value is modified with an XOR before going to h
, but otherwise it’s not.
Above where TEST_VAL
is set, there’s another block that seems to always set tst
to finish
:
always @(posedge clk) begin
if (rst | start) begin
m <= {MSG_BITS{1'b0}};
cnt <= {CNT_BITS{1'b0}};
t <= {(W*2){1'b0}};
f <= 1'b0;
tst <= finish;
end else begin
if (dv_in) begin
m[((W-cnt)*8) +: 8] <= data_in;
cnt <= next_cnt[CNT_BITS-1:0];
t <= t + 1;
f <= finish;
//$display("%t dmgr din d %h m %h c %h t %h f %b t %b", $time, data_in, m, cnt, t, f, tst);
end else if (finish) begin
f <= 1'b1;
end
end
end
Debug
The README.md
suggested uncommenting prints that would help debug. The one two lines before the XOR will show the value of tst
just before it’s used to determine if there XOR happens. I’ll remove the comment, and run make tests
:
oxdf@hacky$ make tests
iverilog -g2012 -o f_sched.test.out f_sched.v f_sched_tb.v
vvp f_sched.test.out
iverilog -g2012 -o bloke2b.test.out bloke2.v f_sched.v f_unit.v g_over_2.v g.v g_unit.v data_mgr.v bloke2s.v bloke2b.v bloke2b_tb.v
vvp bloke2b.test.out
0 dmgr dout h 3643e6e6054190369109e8ae8bd92ccac59fd49b273ac4ec4e7c59e2f5d73c277de64526d3aa9b32e4d9ac0cf09e33f1b953d6ec3737339526fb41da0981d637 t 0
Received message: 7ց �A�&�377��S��3��
���2���&E�}'<���Y|N��:'�ԟ��,ً�� �6�A��C6
0 dmgr dout h d7c0cf2d4de0e6554335e8778d9705cb0e4524b70f1adaa0e2b730ce92bd3a9210fd6752778adc6971e48b5b3e23fba379e1a042c2cc387916160f0f5d71bd18 t 0
Received message: �q]y8��B��y��#>[��qi܊wRg��:���0���␦�$E���w�5CU��M-���
0 dmgr dout h 51f39383b141688a26ea6d793315bbfe30de9f8689fa95656ad55fb143beef9cd3a8781ec867769624dba3c97027b39f1688dbd0f419bcb4337328112bcfd230 t 0
Received message: 0��+(s3����ۈ��'pɣ�$�vg�x�Ӝ�C�_�je������0��3ym�&�hA����Q
iverilog -g2012 -o bloke2s.test.out bloke2.v f_sched.v f_unit.v g_over_2.v g.v g_unit.v data_mgr.v bloke2s.v bloke2b.v bloke2s_tb.v
vvp bloke2s.test.out
0 dmgr dout h 20ecc2ab573de8c8c10325e24d78337b8d70faa8d9b58d8c70c0d0334613f4c1 t 0
Received message: ��F3��p���٨�p�{3xM�%���=W���
0 dmgr dout h 72760e4666c27397f0756c40c493e163aa72b446da0500f9091efb0328fea26e t 0
Received message: n��(� � �F�r�c��@lu�s�fFvr
0 dmgr dout h 62b5ce3f60aa773f4fbcac48c55cb8acac7daea262e361cd2402610fa4039d0c t 0
Received message:
��a$�a�b��}���\�H��O?w�`?εb
rm f_sched.test.out bloke2s.test.out bloke2b.test.out
It’s a bit messy, but looking at the lines starting with “0 dmgr dout h”, it shows that h_in
is changing, but tst
is always 0!
Solve
I’ll go above to to where tst
is set, and fix it to be 1:
always @(posedge clk) begin
if (rst | start) begin
m <= {MSG_BITS{1'b0}};
cnt <= {CNT_BITS{1'b0}};
t <= {(W*2){1'b0}};
f <= 1'b0;
tst <= 1'b1; // finish;
end else begin
Now when I run the tests, not only it is one, but one of the tests prints the flag: