Skip to content
Article
Authors
Published on

PaCoin - CyHub CTF 2023

Article
Authors

This is the write-up for the PaCoin Steganography Challenge from CyHub 2023's Hac-Man CTF event. The organizers set it up to be tricky, weaving through complicated situations like analyzing the attached file and understanding the concept of candlesticks charts.


PaCoin

INFO

Below is the PaCoin challenge's solution from the CyHub 2023 CTF (Hac-Man) that occurred on November 17-18, 2023.

Description:

PUMP IT on PacNance exchange
PUMP IT on PacNance exchange

Hints:

  1. You need to ask yourself 2 questions.
  • What is this data?
  • What is the format of this data?
  1. What is the task name?

  2. Updated description is a hint

Attached File:

PaCoin.csv

Solution

We started by analyzing the attached file, which contained the contents below (only shown the first few lines):

012345
017002979053133732043.031337.032043600
117002979653204332683.8631541.031541667
217002980253154131541.030197.0731131415
317002980853113132346.2630819.6932026491
417002981453202633631.5631065.2232652390
517002982053265233577.4532325.4833245785
617002982653324533909.931913.732565542
717002983253256533216.330896.4431852303

We were literally staring at this document for a whole hour and here's what we understood.

By looking at this, you might as well realize that the first column is the row ID or number, and the second column (if you have ever seen one before) is a Unix Timestamp and you may also notice that it's increasing as the ID increase, which means that this is a record of something over a specific period of time, with each entry corresponding to a unique moment in that timeframe.

Now comes the tough part: understanding what the 3rd, 4th, 5th, 6th, and 7th columns are used for. As the title states, it's a coin, probably a cryptocurrency, but more is needed to understand how we can get the flag out of this.

One of our team members had a familarity with cryptocurrencies and he realized that this is a chart report for a specific time period, here's how he understood it.

Initially, we observed that the value in the 6th column corresponds to the value in the 3rd column of the subsequent line in the CSV file.

IDTimestamp34567
017002979053133732043.031337.032043600
117002979653204332683.8631541.031541667
217002980253154131541.030197.0731131415
317002980853113132346.2630819.6932026491

For example 1st row's 6th column is 31541 which corresponds to the value in the 3rd column of the 2nd line which is also 31541.

This brought him to thinking that this is a Candlestick Chart data, because in a typical candlestick chart, the closing value of one candlestick becomes the opening value of the next candlestick. This relationship in a candlestick chart helps visually represent the continuity of price movements between consecutive time periods.

Here's what it looks like:

Candlestick Chart

And from there, we could already name all of the remaining untitled columns.

IDTimestampOpenHighLowCloseVolume
017002979053133732043.031337.032043600
117002979653204332683.8631541.031541667
217002980253154131541.030197.0731131415
317002980853113132346.2630819.6932026491
417002981453202633631.5631065.2232652390
517002982053265233577.4532325.4833245785

Since we already have all the information needed we have moved forward and tried to represent its chart by using some website like OneCharts, and here's what the whole thing looked like:

PaCoin Chart

From here, we thought that we could do some tricks to get the data to be represented by 0s and 1s. The trick was to convert the red ones to 0s and the green ones to 1s. Red indicates a lower open value than close, while green indicates a higher open value than close.

To do this we first converted the CSV file into JSON (it's possible to achieve the same without converting it from CSV to JSON) using some random website.

json
[
  {
    "ID": 0,
    "Timestamp": 1700297905,
    "Open": 31337,
    "High": 32043,
    "Low": 31337,
    "Close": 32043,
    "Volume": 600
  },
  {
    "ID": 1,
    "Timestamp": 1700297965,
    "Open": 32043,
    "High": 32683.86,
    "Low": 31541,
    "Close": 31541,
    "Volume": 667
  },
  ...
]
[
  {
    "ID": 0,
    "Timestamp": 1700297905,
    "Open": 31337,
    "High": 32043,
    "Low": 31337,
    "Close": 32043,
    "Volume": 600
  },
  {
    "ID": 1,
    "Timestamp": 1700297965,
    "Open": 32043,
    "High": 32683.86,
    "Low": 31541,
    "Close": 31541,
    "Volume": 667
  },
  ...
]

Then we wrote a small JS script that will do the hard work for us.

js
str = '';

for (const item of data) {
   if (item.Open <= item.Close) {
      str += '0'
   } else {
      str += '1'
   }
}
str = '';

for (const item of data) {
   if (item.Open <= item.Close) {
      str += '0'
   } else {
      str += '1'
   }
}

This script sets 0s for Red cells and 1s for Green cells.

Then we got our result which looked like this:

10011100100001101001011110001010100111011000010010111100110010111011000110111011101100111100110010101100101010111100111010111100101101001100101010100000110010111010110111001100101000001011100110101010101100011010000010111101101010111011110010000010
10011100100001101001011110001010100111011000010010111100110010111011000110111011101100111100110010101100101010111100111010111100101101001100101010100000110010111010110111001100101000001011100110101010101100011010000010111101101010111011110010000010

Then we used a pretty famous tool for cryptography called CyberChef to convert it from binary to ASCII.

PaCoin Attempt 1

Hmm... This doesn't look like a flag, is it?

We didn't gave up and tried to instead set 1s for Red cells and 0s for Green cells, and this time we used this updated script.

js
str = '';

for (const item of data) {
   if (item.Open <= item.Close) {
      str += '1'
   } else {
      str += '0'
   }
}
str = '';

for (const item of data) {
   if (item.Open <= item.Close) {
      str += '1'
   } else {
      str += '0'
   }
}

This script sets 1s for Red cells and 0s for Green cells.

Then we got our new result which looked like this:

01100011011110010110100001110101011000100111101101000011001101000100111001000100010011000011001101010011010101000011000101000011010010110011010101011111001101000101001000110011010111110100011001010101010011100101111101000010010101000100001101111101
01100011011110010110100001110101011000100111101101000011001101000100111001000100010011000011001101010011010101000011000101000011010010110011010101011111001101000101001000110011010111110100011001010101010011100101111101000010010101000100001101111101

And now, when we tried to convert it from binary to ASCII we finally got the flag 🎉🎉 and were able to submit it 5 minutes before the end of the CTF challange.

PaCoin Attempt 2

cyhub{C4NDL3ST1CK5_4R3_FUN_BTC}
cyhub{C4NDL3ST1CK5_4R3_FUN_BTC}

Conclusion

This was a very interesting steganography challenge, we had submitted the flag 5 minutes before the end of the CTF challange and jumped from 10th place to 7th just because of this task.