Wykradziono bazę danych LinkedIn (znowu)

Jak donosi DailyDarkWeb rzekomo wykradziono bazę danych serwisu LinkedIn z 2023 roku z 2.5 milionami rekordów. Jeśli to prawda, to będzie oznaczało, że LinkedIn postanowiło kontynuować bogatą tradycję, w której co ileś lat wykradane są miliony danych profilowych z ich serwisu.

Steganografia i jej analiza w Pythonie

Zgłosił się do mnie klient z projektem, którego celem było dojście do tego, czy w danym obrazku nie znajdują się ukryte informacje zakodowane poprzez steganografię.

Steganografia Kodek Online

Poniżej prezentuję skrypt w Pythonie, który wyciąga piksele w trybach horyzontalnych (czyli piksel po pikselu na wysokość obrazka) i wertykalnych z grafiki:

  • najmniej znaczące bity w składowych R
  • najmniej znaczące bity w składowych G
  • najmniej znaczące bity w składowych B
  • najmniej znaczące bity w składowych ALPHA
  • kombinowane najmniej znaczące bity w RGB
  • kombinowane najmniej znaczące bity w BGR
  • kombinowane najmniej znaczące bity w RGBA
  • kombinowane najmniej znaczące bity w ABGR

Są to najczęściej stosowane metody używane przez narzędzia do steganografii do ukrywania rozbitych danych w najmniej znaczących bitach składowych kolorów RGB.

from PIL import Image
from bitarray import bitarray


def dump(filename, content):
    f = open(filename, 'wb')
    f.write(content)
    f.close()


def stego(filename, filename_prefix):
    im = Image.open(filename)
    pixels = im.load()

    width, height = im.size

    #
    # RED, GREEN, BLUE least significant bits
    #
    byte1, byte2, byte3, byte4, bits = 0, 0, 0, 0, 0
    out1, out2, out3, out4 = bytearray(), bytearray(), bytearray(), bytearray()
    bit1, bit2, bit3, bit4 = bitarray(), bitarray(), bitarray(), bitarray()
    all1, all2, all3, all4 = bitarray(), bitarray(), bitarray(), bitarray()

    rgb, rgba = bitarray(), bitarray()

    xx = width
    yy = height
    horizontal = True

    if filename_prefix == "vertical":
        xx = height
        yy = width
        horizontal = False

    for x in range(xx):
        for y in range(yy):

            if horizontal is True:
                pixel = pixels[x, y]
            else:
                pixel = pixels[y, x]

            # separate R G B
            bit1.append(pixel[0] & 1)
            bit2.append(pixel[1] & 1)
            bit3.append(pixel[2] & 1)
            bit4.append(pixel[3] & 1)

            # combined RGB
            all1.append(pixel[0] & 1)
            all2.append(pixel[1] & 1)
            all3.append(pixel[2] & 1)
            all4.append(pixel[3] & 1)

            # RGB
            rgb.append(pixel[0] & 1)
            rgb.append(pixel[1] & 1)
            rgb.append(pixel[2] & 1)

            # RGBA
            rgba.append(pixel[0] & 1)
            rgba.append(pixel[1] & 1)
            rgba.append(pixel[2] & 1)
            rgba.append(pixel[3] & 1)

            # compose 8 bits
            if bits < 8:
                byte1 = (byte1 << 1) | (pixel[0] & 1)
                byte2 = (byte2 << 1) | (pixel[1] & 1)
                byte3 = (byte3 << 1) | (pixel[2] & 1)
                byte4 = (byte4 << 1) | (pixel[3] & 1)
                bits = bits + 1
            else:
                bits = 0
                out1.append(byte1 & 0xFF)
                out2.append(byte2 & 0xFF)
                out3.append(byte3 & 0xFF)
                out4.append(byte4 & 0xFF)

    dump(filename_prefix + "_1_RED.bin", out1)
    dump(filename_prefix + "_1_GREEN.bin", out2)
    dump(filename_prefix + "_1_BLUE.bin", out3)
    dump(filename_prefix + "_1_ALPHA.bin", out4)

    dump(filename_prefix + "_2_RED_BITS.bin", bit1)
    dump(filename_prefix + "_2_GREEN_BITS.bin", bit2)
    dump(filename_prefix + "_2_BLUE_BITS.bin", bit3)
    dump(filename_prefix + "_2_ALPHA_BITS.bin", bit4)

    bit1.reverse()
    bit2.reverse()
    bit3.reverse()
    bit4.reverse()

    dump(filename_prefix + "_3_RED_REV_BITS.bin", bit1)
    dump(filename_prefix + "_3_GREEN_REV_BITS.bin", bit2)
    dump(filename_prefix + "_3_BLUE_REV_BITS.bin", bit3)
    dump(filename_prefix + "_3_ALPHA_REV_BITS.bin", bit4)

    dump(filename_prefix + "_4_RED_BITS_RGB.bin", all1)
    dump(filename_prefix + "_4_GREEN_BITS_RGB.bin", all2)
    dump(filename_prefix + "_4_BLUE_BITS_RGB.bin", all3)
    dump(filename_prefix + "_4_ALPHA_BITS_RGB.bin", all4)

    all1.reverse()
    all2.reverse()
    all3.reverse()
    all4.reverse()

    dump(filename_prefix + "_5_RED_BITS_BGR.bin", all1)
    dump(filename_prefix + "_5_GREEN_BITS_BGR.bin", all2)
    dump(filename_prefix + "_5_BLUE_BITS_BGR.bin", all3)
    dump(filename_prefix + "_5_ALPHA_BITS_BGR.bin", all4)

    dump(filename_prefix + "_6_RGB_ALL_BITS.bin", rgb)
    dump(filename_prefix + "_6_RGBA_ALL_BITS.bin", rgba)

    rgb.reverse()
    rgba.reverse()

    dump(filename_prefix + "_6_RGB_ALL_REV_BITS.bin", rgb)
    dump(filename_prefix + "_6_RGBA_ALL_REV_BITS.bin", rgba)


if __name__ == '__main__':
    stego('IMG1.PNG', 'horizontal')
    stego('IMG1.PNG', 'vertical' )

Może komuś się kiedyś przyda.

Samurai Cop

Nie wiem dlaczego nigdy tego nie widziałem, ale to prawdziwa gratka dla fanów samurajów, kina akcji lat 90 i kiczowatego poczucia humoru 🙂

Nic lepszego dzisiaj nie zobaczysz :D, przy okazji świetny plakat promujący

Upadek StackOverflow

Już dawno się przekonałem, że StackOverflow to miejsce, gdzie jedynie zaglądam jak już naprawdę nigdzie, nigdzie indziej nie znajduję rozwiązania programistycznych problemów.

Jednak przestałem już lata temu cokolwiek samemu tam pisać z prostej przyczyny. Miejsce to stało się moderacyjnym piekłem, gdzie moderatorzy poprawiają każdą możliwą odpowiedź. Dodadzą przecinki, zmienią skróty, zmienią nawet techniczne zwroty i kod.

Tak – zmieniają kod w moich i zapewne twoich odpowiedziach, sprawdź sam jeśli kiedykolwiek tam pisałeś, możesz się zdziwić. To główna przyczyna, dla której przestałem tam się udzielać.

Dla rozrywki przywracałem stan jednej z moich oryginalnych odpowiedzi, żeby pokazać co tam się odjaniepawla:

Ponad 17 razy przywracałem moją oryginalną odpowiedź, która latami była pierwszą odpowiedzią, a moderatorzy non stop mi ją modyfikowali.

W końcu dostałem bana… Można się tego było spodziewać, ale warto nad tym pomyśleć następnym razem gdy zechcesz cokolwiek tam odpowiedzieć – czy warto rozbudowywać miejsce, gdzie chcą twoich odpowiedzi, ale nie do końca takich jakie je piszesz. Absurd.

Złote gacie dla moderatora roku

Miejsce, które miało być przystanią dla programistów – zostało piekiełkiem dla moderatorów wyjętych żywcem z Wikipedii (przy okazji przypominam dlaczego nie warto wspierać finansowo Wikipedii).

Joel Spolsky i Jeff Atwood sprzedali w 2021 StackOverflow za 1.8 miliarda dolarów (przy okazji obrazili się na Elona Muska i oboje przestali pisać na Twitterze i przenieśli się na Mastodona) i mają wyjebane jajca, a moderatorzy i boty skutecznie przeczesują każdą odpowiedź i nabijają sobie punkty na edycjach żeby potem kandydować na moderatora roku…

Stare odpowiedzi, a nowe technologie

Od jakiegoś czasu zauważyłem, że na StackOverflow i satelickich stronach z tego uniwersum coraz częściej można dostrzec brak zaktualizowanych odpowiedzi na zmieniające się technologie programistyczne.

Nowe frameworki i wersje języków programowania oferują nowe rozwiązania starych problemów, a ich opisów brak, przykładem może być składnia Python 2 vs 3 czy np. używany przeze mnie framework PHP Yii2. Myślę, że to nie przypadek, że ludzie przestali pisać odpowiedzi i nie widzę, żeby się to zmieniło na plus w przyszłości – wręcz odwrotnie.

A jakie wy macie doświadczenia ze StackOverflow?