Wyjaśnienie konkursu od Gynvael Coldwind

Wyjaśnienie konkursu od Gynvael Coldwind
pingwindyktator
  • Rejestracja:ponad 12 lat
  • Ostatnio:20 dni
  • Lokalizacja:Kraków
  • Postów:1055
12

(Właściwie nie mam pomysłu do jakiego działu to wrzucić)

Kilka osób było zainteresowanych, więc bardzo proszę.

Disclaimer: przedstawiam rozwiązania z punktu widzenia kogoś, kto zna się na kryptografii, trochę zna się na ogólnie rozumianym bezpieczeństwie, natomiast na pewno nie zna się na javascript. Dla mnie właśnie to stanowiło największy problem, większość czasu spędziłem grzebiąc w dokumentacji js. Smutne. Nie odpowiadam zatem za błędy merytoryczne związane z js. Mimo wszystko uważam, że moje rozwiązania pokazują ciekawe podejście, tzn okazuje się, że bez znajomości języka można zrobić dość zaawansowaną analize kodu w tym języku i coś w stylu reverse engineer, co na pewno łatwe nie jest.

Zachęcam do zrobienia tego samodzielnie, dużo funu jest moim zdaniem w takich zagadkach, więc jeśli ktoś w ogóle tego nie ruszał, to niech chociaż spróbuje.

CZĘŚĆ 1: http://gynvael.coldwind.pl/xacrackme/
Można to rozwiązać co najmniej na 3 sposoby (a przynajmniej ja na 3 sposoby wpadłem). Przedstawię rozwiązanie najszybsze, najsprytniejsze.
Zobaczmy co dzieje się od samego początku, tj. od momentu kliknięcia przycisku Submit/Prześlij

Kopiuj
1. form.addEventListener('submit', function (e) {
2.           e.preventDefault();
3.           password = getPass().toLowerCase();
4.           if (sha256(password) === HASH) {
5.             console.info('Valid password: %s', password);
6.             alert('Success!');
7.             location.href = password + "k.html";
8.           } else {
9.             error.classList.add('visible');
10.            clearTimeout(tid);
11.            tid = setTimeout(function () {
12.              error.classList.remove('visible');
13.            }, 1500);
14.            console.warn('Wrong password: %s', password);
15.          }
16.        }

Widzimy, że wielkość liter w haśle prawdopodobnie nie ma znaczenia. Nie wiemy jeszcze co robi funkcja getPass, nie jest to natomiast nam potrzebne, zresztą tak samo jak ta uwaga (co okaże się poźniej), ale jest ona potencjalnie bardzo ważna, dlatego o tym wspominam.
Można przypuszczać, że stroną docelową, naszym celem jest location.href. Hasło sprawdzane jest w sposób tradycyjny: przechowywany jest hasz hasła, poźniej haszowane jest wpisane hasło (w tym wypadku wynik funkcji getPass) i owe hashe są porównywane. Hash jednak jest zazwyczaj problemem nie do przeskoczenia.
const HASH = "aaff6f579e5125ce8c8824b9dbc4c2bec20727f0558b914bd6ee910e1b3348ce";
Mimo wszystko spróbujmy szczęścia. Oczywiście istnieją przecież jakies "bazy danych" dla sha256...
No i okazuje się, że https://crackstation.net/ jest przekonany, że odpowiedzią jest basilis.
location.href = password + "k.html";
Sprawdźmy zatem: http://gynvael.coldwind.pl/xacrackme/basilisk.html
Bingo. Jeśli ktoś w tym momencie zwątpił w bezpieczeństwo sha256 i swoich haseł, to uspokajam.
Trochę o samych funkcjach haszujących:
funkcja haszująca to najczęsciej funkcja z jakiegoś zbioru X w skończony podzbiór liczb naturalnych P z założeniem, że moc zbioru X jest sporo większa od mocy zbioru P. X'em może być cokolwiek - inne liczby, stringi, hasła. Proste funkcje hashujące są używane w tablicach haszujących (std::unordered_map w C++), natomiast sha2 jest przykładem funkcji haszującej kryptograficznie bezpiecznej. Co to znaczy? To, że funkcja ta może być uważana za "jednokierunkową", tzn łatwo jest obliczyć sha(jakis_string), natomiast mając wynik tej operacji odzyskanie jakis_string jest praktycznie niemożliwe. Dlatego jest to bezpieczne. Matematycznie ściśle mówiąc, nie wiemy, czy jest to funkcja jednokierunkowa, nie wiemy w ogóle, czy takowe istnieją. W praktyce były przypadki wykazania słabości funkcji kiedyś uważanych za kryptograficznie bezpieczne (md2, md4, md5, sha1). sha2 (sha256 i sha512) jednak trzyma się dobrze.
Mamy 3 podstawowe sposoby przechowywania haseł użytkowników.

  • sposób bardzo głupi: plaintext. Niestety część serwisów dalej z tego korzysta (raz miałem sytuację, w której zapomniałem hasła i wysłali mi je na maila Oo)
  • sposób głupi: trzymanie haszy haseł. Jasne jest jak weryfikujemy poprawność hasła, którym próbuje uwierzytelnić się użytkownik. Nie jest to jednak najlepszy sposób z dwóch powodów (a przynajmniej dwa w tym momencie przychodzą mi na myśl). Po pierwsze - słabe, krótkie hasła można złamać korzystając chociażby z tej bazy, którą wykorzystałem do złamania etapu pierwszego tego konkursu. Po drugie - mając identyczne hasze wiemy, że hasła również są identyczne. Tutaj dorzucamy troche heurystyki, jakieś statystyki najpopularniejszych haseł i już.
  • sposób mądry (trochę w uproszczeniu): dla każdego hasła p trzymamy unikalny ciąg "losowych" znaków s. Tak zwyczajnie, w plaintexcie. Obok tego trzymamy hasz z h + s. Teraz kiedy użytkownik się loguje, doklejamy s do jego hasła i sprawdzamy się się zgadza. Bardzo proste, a eliminuje dwa najpoważniejsze słabości metody powyżej.
    Każdy poważny serwis / os / program / cokolwiek używa trzeciego sposobu.

Wracając do konkursu.
CZĘŚĆ 2:
Tak jak wcześniej, spróbujmy analizować wykonanie kodu od momentu wciśnięcia "Decode".

Kopiuj
var decodingFn = function () {};

1. document.querySelector('form').addEventListener('submit', function (e) {
2.             e.preventDefault();
3.             runDecoding();
4.           }

1.       function runDecoding() {
2.         createDecodingFn();
3.         decodingFn();
4.       }

1.       function createDecodingFn() {
2.         try {
3.           eval(XORCipher.decode(document.querySelector(p2.value).innerHTML.substr(0,13) + '...', SECRET1));
4.         } catch(e) {
5.           window.decodingFn = function () {}
6.         }
7.       }

Widzimy ciekawą rzecz. Funkcja decodingFn nie jest explicite podana, jest tworzona dynamicznie (createDecodingFn). Najbardziej interesuje nas zatem:

Kopiuj
eval(XORCipher.decode(document.querySelector(p2.value).innerHTML.substr(0,13) + '...', SECRET1))

Funkcja eval wykonuje kod, jaki dostała jako argument. Można zatem przypuszczać, że będzie to coś w stylu window.decodingFn = function () {[...]. Okej, wiemy mniej więcej co ma być argumentem. Lecimy dalej.
http://gynvael.coldwind.pl/xacrackme/XORCipher.js
Z tego potrzebne będzie nam jedynie to, że pierwszym argumentem funkcji z XORCipher jest klucz do szyfrowania / odszyfrowania.

Kopiuj
XORCipher.decode(document.querySelector(p2.value).innerHTML.substr(0,13) + '...', SECRET1)

Z tego co zrozumiałem, funkcja querySelector "chwyta" obiekt, którego id, nazwe, cośtam podaliśmy jako argument i nam go oddaje. Mały research i mamy http://www.w3schools.com/jquery/jquery_ref_selectors.asp
W polu p2 (pass 2) musi być zatem jakiegoś typu selector. Można strzelać, że chodzi o wybranie konkretnego elementu po jego id, sensownie to brzmi. Kiedy już mamy nasz obiekt, robimy na nim innerHTML. Działa to chyba troche tak, jakbyśmy brali kod HTML, który jest pod spodem naszego obiektu (który tworzy, definiuje ten obiekt). To ma sens. Próbujemy, odpalamy konsole w przeglądarce, w pass 2 wklepujemy jakiś id-selector, strzelamy i dla wartości "#legend": bingo2. Mamy jedno hasło i funkcję dekodującą.

Kopiuj
1. window.decodingFn = function() {
2.   try { decodedData = self[p4.value](basilisk, p3.value) || []; }
3.   catch(e) { decodedData = []; }
4. 
5.   ctx2.clearRect(0, 0, C_SIZE, C_SIZE);
6. 
7.   if (XORCipher.encode(document.title, genKey()) === SECRET2) { yaaay(); }
8.   else { boooo(); }
9. }

W sumie dalej nie rozumiem linii 2,3, tego self, skąd ono się wzięło i dlaczego jest tablicą(?), więc uznałem, że zajmę się linią 7. A ta jest wyjątkowo prosta i niesie dużo informacji.

Kopiuj
1.       function genKey() {
2.         if (!arguments.length) {
3.           return [arguments.length === 0, p2.value, p3.value, p4.value].join(':');
4.         }
5.       }

arguments też nie rozumiem, ale ważne jest to, że wynikiem wywołania tej funkcji jest coś, z czego łatwo odzyskamy p3.value, p4.value. Tutaj wystarczy nie być łomem z matematyki, żeby zauważyć, że wystarczy po prostu wykonać komende

Kopiuj
XORCipher.decode(document.title, SECRET2)

żeby odzyskać zakodowany "poprawny" wynik funkcji genKey. No i mamy:
"true:#legend:btoa:decodeImage"
Mamy hasło trzecie i hasło czwarte. Prawie koniec. Poszukajmy gdzie używane jest hasło 1.

Kopiuj
ctx2.globalAlpha = +p1.value || 0;

strzelam, że chodzi o jakąś transparencje, czysto techniczna rzecz związana z rysowaniem obrazu. Ustawiam na 1. Bo tak mi się wydaję.
Działa? Działa. Mamy bazyliszka. Rzeczywiście najładniejszy to on nie jest ;D

wołam @Shalom @Rev @msm @Gynvael Coldwind @Hepek @TLesiu @Sh4rk1 bo chyba zainteresowani.


do not code, write prose
edytowany 1x, ostatnio: pingwindyktator
Zobacz pozostałe 2 komentarze
hauleth
Ogólnie bezpieczniej było pominąć cały ten wywód o hashach, bezpieczniej by było.
pingwindyktator
O chryste panie co za herezje....
pingwindyktator
@winerfresh przemyśl to jeszcze raz i jeśli uznasz, że dalej masz rację to daj znać, postaram się rozwiać te przekonanie. Ale dam Ci szanse.
hauleth
Żadna funkcja ze zbioru nieskończonego na skończony nie może być funkcją odwracalną. To jest matematycznie niemożliwe ze względu na zasadę szufladkowania Dirichleta. Jeśli jesteś w stanie mi udowodnić, że jest inaczej, to chętnie to zobaczę. To są podstawy matematyki dyskretnej, a jeszcze nawet nie zaczęliśmy tu mówić o kryptografii. To, że w jakimś zbiorze elementów potrafimy szybko znaleźć kolizję mówi tylko o tym, że funkcja nie jest już bezpieczna, a nie, że jest odwracalna.
hauleth
@pingwindyktator jak chcesz przykład funkcji nieodwracalnej (na 100 procent, z dowodem jeśli trzeba) to Adler32. Nie jest ona co prawda kryptograficznie bezpieczna, ale na 100% nieodwracalna.
S4
  • Rejestracja:ponad 9 lat
  • Ostatnio:około 9 lat
  • Postów:13
0

dobra robota @pingwindyktator :)

pingwindyktator
  • Rejestracja:ponad 12 lat
  • Ostatnio:20 dni
  • Lokalizacja:Kraków
  • Postów:1055
0

@winerfresh

  1. "Żadna funkcja ze zbioru nieskończonego na skończony nie może być funkcją odwracalną. "
    To racja - niestety tutaj prawdziwość Twoich wywodów się kończy. Dawno nie rozdziabiłem tam bardzo ust czytając coś, czego nie pisał anonimowy burak / troll.

1.1 " To jest matematycznie niemożliwe ze względu na zasadę szufladkowania Dirichleta."
Bzdury - zasada ta mówi WYŁĄCZNIE o zbiorach skończonych.

  1. "Jeśli jesteś w stanie mi udowodnić, że jest inaczej, to chętnie to zobaczę."
    To coś w stylu "jeśli jesteś w stanie udowodnić, że 2+2 != 4 w ciele liczb rzeczywistych z tradycyjnym dodawaniem i mnożeniem, to chętnie to zobaczę". Bezsens kompletny.

  2. "ta funkcja jest jednokierukowa", "wiemy, że są jednokierunkowe i wiemy, że takowe funkcje istnieją".
    Uściślając: funkcja jednokierunkowa f: X -> Y to taka funkcja, której wartość f(x) znajdziemy w czasie wielomianowym dla każdego x, natomiast znalezienie wartości f^(-1)(y) wymaga czasu dłuższego niż wielomianowy ("dłuższego" w rozumieniu notacji Theta) dla dowolnego y. Twierdzisz, że takie funkcje istnieją. Jeśli potrafisz - udowodnij proszę.
    https://en.wikipedia.org/wiki/P_versus_NP_problem
    Teza istnienia takowych funkcji implikuje wprost, że P != NP. Zgłoś się po swoje milion dolarów.
    https://en.wikipedia.org/wiki/Millennium_Prize_Problems
    Niestety kontr-dowodu nie mam. Bo nie istnieje. Cały świat matematyków nie wie czy takie funkcje są.
    wiki: "If f is a one-way function, then the inversion of f would be a problem whose output is hard to compute (by definition) but easy to check (just by computing f on it). Thus, the existence of a one-way function implies that FP≠FNP, which in turn implies that P≠NP. However, it is not known whether P≠NP implies the existence of one-way functions."
    "The existence of such one-way functions is still an open conjecture."
    https://en.wikipedia.org/wiki/One-way_function

Poza tym - mieszasz terminy. funkcja nieodwracalnej to funkcja nie będąca bijekcją, czyli funkcja, której odwrotność nie istnieje. Jeżeli chcesz podać mi przykład takiej funkcji to nie, dzięki.

  1. "Przetrzymywanie (nawet kryptograficznych) szybkich haszy haseł to zły pomysł. Od tego mamy osobną grupę funkcji o nazwie KDF (też hashe, ale o dodatkowych właściwościach, np. są bardzo wolne)." twierdzisz, że sha2 jest jednokierunkowe, ale z jakiegoś powodu powinno używać się czegoś innego do przechowywania haseł w bazie danych. Dlaczego? Coś tu nie gra.

wołam @bogdans bo z tego co zdążyłem zauważyć, to znasz się na matematyce. Może się zainteresujesz.


do not code, write prose
edytowany 6x, ostatnio: pingwindyktator
bogdans
@pingwindyktator, to nie moja działka matematyki, ale @winerfresh wyraźnie nie zna definicji funkcji jednokierunkowej. P.S. przywoływanie nie działa.
hauleth
Moderator
  • Rejestracja:około 17 lat
  • Ostatnio:14 dni
1

@pingwindyktator

1.1 Zasada ta mówi o czymkolwiek, zwłaszcza o liczbach kardynalnych.
2. i 3. Funkcja jednokierunkowa znaczy, że nie istnieje funkcja do niej odwrotna (a nie nic o złożoności obliczeniowej tejże, a nawet jeśli, to matematyka dalej temu przeczy). Przykładem takiej funkcji jest funkcja stała, signum, etc. Każda z nich ma taką samą własność jak każda funkcja skrótu - zamienia wartość ze zbioru nieskończonego w skończony. Jeśli za to jest milion dolarów, to ja chętnie.
4. scrypt, bcrypt, a jeśli bozia tamtych nie dała, to od biedy może być PBKDF2: https://gist.github.com/paragonie-scott/e9319254c8ecbad4f227#i-general-mistakes lub http://codahale.com/how-to-safely-store-a-password/

PS Mimo wszystko gratulacje za rozwiązanie.


edytowany 2x, ostatnio: hauleth
pingwindyktator
  • Rejestracja:ponad 12 lat
  • Ostatnio:20 dni
  • Lokalizacja:Kraków
  • Postów:1055
0

@winerfresh koleś, ja mówie o czymś zupełnie innym. Nie ma czegoś takiego jak funkcja jednostronna chryste panie. Mówię o funkcji ** jednokierunkowej **, której definicję Ci przedstawiłem.

  1. zgadzam się, że istnieją lepsze metody przechowywania haszy, natomiast po pierwsze nie powiesz mi, że sha2 + salt jest głupie, po drugie chciałem odnieść się konkretnie do sha2, coby nie okazało się, że jest durnym wymysłem (bo przecież "złamałem" w 10 sekund).

do not code, write prose
edytowany 1x, ostatnio: pingwindyktator
WhiteLightning
@pingwindykator: ktore jest najlepsze (a moze jeszcze inne)? sha2(string) + salt czy sha2(string)+sha2(salt) czy sha2(string+salt)
pingwindyktator
@WhiteLightning zauważ, że tylko trzecie rozwiązanie, hasz(string+salt), naprawia dwa wspomniane przeze mnie problemy. Oczywiście jest to uproszczenie sprawy, możemy użyć innych niż sha2 (sha3 chociażby) funkcji, możemy doczepiac salt w inny sposób, w każdym razie ten schemat jest dobry.
hauleth
@WhiteLightning zauważ, że H(pass) + H(salt) nie różni się niczym od H(pass) (atakujący szybko może zauważyć, że długość jest niepoprawna). Dodatkowo najlepsze jest wspomniane wyżej przeze mnie scrypt a nie SHA, które nie były opracowywane jako KDF.
Gynvael Coldwind
  • Rejestracja:ponad 21 lat
  • Ostatnio:13 dni
  • Lokalizacja:Zurich, Switzerland
  • Postów:457
1

Krótkie sprostowanie:
Organizatorem konkursu (i jednocześnie fundatorem nagród, które już były rozdane przedwczoraj) jest Xa (Sebastian Rosik), czyli autor okładki.
Więc technicznie to jest "Wyjaśnienie konkursu od Xa" ;)


peace,
gynvael.coldwind//vx "Imagination is more important than knowledge..." Albert Einstein
freemp3
  • Rejestracja:około 11 lat
  • Ostatnio:około 11 godzin
  • Lokalizacja:Miechów
  • Postów:284
0

Tak w zasadzie to organizatorem lub współorganizatorem konkursu jest chyba firma RST (rst.com.pl), ponieważ to jej adres e-mail był podany jako kontaktowy ;) Kto wysłał wiadomość z rowiązaniem ten zapewne już dostał informację zwrotną, w tym i ja ;), lub niebawem dostanie, że rozwiązaniem jest w.w obrazek z bazyliszkiem, a nagrody dostało pierwsze 15 osób. Niestety ja się nie załapałem ;)


edytowany 1x, ostatnio: freemp3
Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)