I think you need numpy.where if same length and same index values in both DataFrames with comparing both columns with DataFrame.all:
df1['d'] = np.where((df1[['a', 'b']] == df2[['a', 'b']]).all(axis=1), df2['d'], df1['d'])
print (df1)
a b c d
0 1 2 5 2
1 1 5 5 1
2 2 3 4 4
print (df1[['a', 'b']] == df2[['a', 'b']])
a b
0 True True
1 True False
2 True True
print ((df1[['a', 'b']] == df2[['a', 'b']]).all(axis=1))
0 True
1 False
2 True
dtype: bool
Another more general solution for matching by merge with left join, but is necessary unique rows in df2 by columns a and b by drop_duplicates, last combine_first and remove unnecessary column d_:
df = (df1.merge(df2.drop_duplicates(['a','b']), on=['a','b'], how='left', suffixes=('_',''))
.assign(d= lambda x: x['d'].combine_first(x['d_']))
.drop('d_', axis=1))
print (df)
a b c d
0 1 2 5 2.0
1 1 5 5 1.0
2 2 3 4 4.0
print (df2.drop_duplicates(['a','b']))
a b d
0 1 2 2
2 2 3 4
print (df1.merge(df2.drop_duplicates(['a','b']), on=['a','b'], how='left', suffixes=('_','')))
a b c d_ d
0 1 2 5 1 2.0
1 1 5 5 1 NaN
2 2 3 4 1 4.0